FieldDelegatingCodeAction.cs 5.2 KB
Newer Older
S
Sam Harwell 已提交
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
2 3 4 5 6 7 8

using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeGeneration;
9
using Microsoft.CodeAnalysis.CodeStyle;
10 11 12
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
13
using Roslyn.Utilities;
14

C
CyrusNajmabadi 已提交
15
namespace Microsoft.CodeAnalysis.GenerateConstructorFromMembers
16
{
C
CyrusNajmabadi 已提交
17
    internal partial class GenerateConstructorFromMembersCodeRefactoringProvider
18 19 20
    {
        private class FieldDelegatingCodeAction : CodeAction
        {
C
CyrusNajmabadi 已提交
21
            private readonly GenerateConstructorFromMembersCodeRefactoringProvider _service;
22 23
            private readonly Document _document;
            private readonly State _state;
24
            private readonly bool _addNullChecks;
25 26

            public FieldDelegatingCodeAction(
C
CyrusNajmabadi 已提交
27
                GenerateConstructorFromMembersCodeRefactoringProvider service,
28
                Document document,
29 30
                State state,
                bool addNullChecks)
31
            {
32 33 34
                _service = service;
                _document = document;
                _state = state;
35
                _addNullChecks = addNullChecks;
36 37 38 39 40 41 42 43 44 45 46
            }

            protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
            {
                // First, see if there are any constructors that would take the first 'n' arguments
                // we've provided.  If so, delegate to those, and then create a field for any
                // remaining arguments.  Try to match from largest to smallest.
                //
                // Otherwise, just generate a normal constructor that assigns any provided
                // parameters into fields.
                var parameterToExistingFieldMap = new Dictionary<string, ISymbol>();
47
                for (int i = 0; i < _state.Parameters.Length; i++)
48
                {
49
                    parameterToExistingFieldMap[_state.Parameters[i].Name] = _state.SelectedMembers[i];
50 51
                }

52
                var factory = _document.GetLanguageService<SyntaxGenerator>();
53

54
                var syntaxTree = await _document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
55 56 57 58
                var options = await _document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
                var preferThrowExpression = options.GetOption(CodeStyleOptions.PreferThrowExpression).Value;

                var compilation = await _document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
59
                var (fields, constructor) = factory.CreateFieldDelegatingConstructor(
60
                    compilation,
61 62 63
                    _state.ContainingType.Name,
                    _state.ContainingType,
                    _state.Parameters,
64 65
                    parameterToExistingFieldMap,
                    parameterToNewFieldMap: null,
66 67
                    addNullChecks: _addNullChecks,
                    preferThrowExpression: preferThrowExpression,
68 69
                    cancellationToken: cancellationToken);

C
CyrusNajmabadi 已提交
70 71 72 73 74 75 76 77
                // If the user has selected a set of members (i.e. TextSpan is not empty), then we will
                // choose the right location (i.e. null) to insert the constructor.  However, if they're 
                // just invoking the feature manually at a specific location, then we'll insert the 
                // members at that specific place in the class/struct.
                var afterThisLocation = _state.TextSpan.IsEmpty
                    ? syntaxTree.GetLocation(_state.TextSpan)
                    : null;

78
                var result = await CodeGenerator.AddMemberDeclarationsAsync(
79 80
                    _document.Project.Solution,
                    _state.ContainingType,
C
CyrusNajmabadi 已提交
81
                    fields.Concat(constructor),
82 83
                    new CodeGenerationOptions(
                        contextLocation: syntaxTree.GetLocation(_state.TextSpan),
C
CyrusNajmabadi 已提交
84
                        afterThisLocation: afterThisLocation),
85
                    cancellationToken).ConfigureAwait(false);
86 87 88 89 90 91 92 93

                return result;
            }

            public override string Title
            {
                get
                {
94 95
                    var symbolDisplayService = _document.GetLanguageService<ISymbolDisplayService>();
                    var parameters = _state.Parameters.Select(p => symbolDisplayService.ToDisplayString(p, SimpleFormat));
96 97
                    var parameterString = string.Join(", ", parameters);

98
                    if (_state.DelegatedConstructor == null)
99
                    {
100
                        return string.Format(FeaturesResources.Generate_constructor_0_1,
101
                            _state.ContainingType.Name, parameterString);
102 103 104
                    }
                    else
                    {
105
                        return string.Format(FeaturesResources.Generate_field_assigning_constructor_0_1,
106
                            _state.ContainingType.Name, parameterString);
107 108 109 110 111
                    }
                }
            }
        }
    }
S
Sam Harwell 已提交
112
}