AbstractUseTypeCodeRefactoringProvider.cs 4.6 KB
Newer Older
1 2 3
// 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;
4
using System.Diagnostics;
5 6 7 8 9
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.Extensions;
J
Julien Couvreur 已提交
10
using Microsoft.CodeAnalysis.CSharp.Syntax;
11
using Microsoft.CodeAnalysis.CSharp.Utilities;
12
using Microsoft.CodeAnalysis.Diagnostics;
13
using Microsoft.CodeAnalysis.Editing;
J
Julien Couvreur 已提交
14
using Microsoft.CodeAnalysis.Options;
15
using Microsoft.CodeAnalysis.Shared.Extensions;
16 17
using Microsoft.CodeAnalysis.Text;

18
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseType
19
{
J
Julien Couvreur 已提交
20 21
    internal abstract class AbstractUseTypeCodeRefactoringProvider : CodeRefactoringProvider
    {
J
Julien Couvreur 已提交
22
        protected abstract string Title { get; }
J
Julien Couvreur 已提交
23 24 25 26
        protected abstract Task HandleDeclarationAsync(Document document, SyntaxEditor editor, SyntaxNode node, CancellationToken cancellationToken);
        protected abstract TypeSyntax FindAnalyzableType(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken);
        protected abstract TypeStyleResult AnalyzeTypeName(TypeSyntax typeName, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken);

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var document = context.Document;
            var textSpan = context.Span;
            var cancellationToken = context.CancellationToken;

            if (!textSpan.IsEmpty)
            {
                return;
            }

            if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles)
            {
                return;
            }

            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
            var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);

            var declaration = GetDeclaration(root, textSpan);
            if (declaration == null)
            {
                return;
            }

            Debug.Assert(declaration.IsKind(SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement, SyntaxKind.DeclarationExpression));

J
Julien Couvreur 已提交
55
            var declaredType = FindAnalyzableType(declaration, semanticModel, cancellationToken);
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
            if (declaredType == null)
            {
                return;
            }

            if (declaredType.OverlapsHiddenPosition(cancellationToken))
            {
                return;
            }

            if (!declaredType.Span.IntersectsWith(textSpan.Start))
            {
                return;
            }

J
Julien Couvreur 已提交
71
            var typeStyle = AnalyzeTypeName(declaredType, semanticModel, optionSet, cancellationToken);
72
            if (typeStyle.IsStylePreferred && typeStyle.Severity != ReportDiagnostic.Suppress)
73
            {
74
                // the analyzer would handle this.  So we do not.
75 76 77
                return;
            }

78
            if (!typeStyle.CanConvert())
79 80 81 82 83 84
            {
                return;
            }

            context.RegisterRefactoring(
                new MyCodeAction(
J
Julien Couvreur 已提交
85
                    Title,
86 87 88 89 90 91 92 93 94 95
                    c => UpdateDocumentAsync(document, declaredType, c)));
        }

        private static SyntaxNode GetDeclaration(SyntaxNode root, TextSpan textSpan)
        {
            var token = root.FindToken(textSpan.Start);
            return token.Parent?.FirstAncestorOrSelf<SyntaxNode>(
                a => a.IsKind(SyntaxKind.DeclarationExpression, SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement));
        }

J
Julien Couvreur 已提交
96
        private async Task<Document> UpdateDocumentAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
97 98 99 100
        {
            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
            var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);

J
Julien Couvreur 已提交
101
            await HandleDeclarationAsync(document, editor, node, cancellationToken).ConfigureAwait(false);
102 103 104 105 106 107 108 109 110 111 112 113 114 115

            var newRoot = editor.GetChangedRoot();
            return document.WithSyntaxRoot(newRoot);
        }

        private class MyCodeAction : CodeAction.DocumentChangeAction
        {
            public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument) :
                base(title, createChangedDocument)
            {
            }
        }
    }
}