~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/nrefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/ExtractMethodAction.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// 
 
2
// ExtractMethodAction.cs
 
3
//  
 
4
// Author:
 
5
//       Mike KrĆ¼ger <mkrueger@xamarin.com>
 
6
// 
 
7
// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com)
 
8
// 
 
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
10
// of this software and associated documentation files (the "Software"), to deal
 
11
// in the Software without restriction, including without limitation the rights
 
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
13
// copies of the Software, and to permit persons to whom the Software is
 
14
// furnished to do so, subject to the following conditions:
 
15
// 
 
16
// The above copyright notice and this permission notice shall be included in
 
17
// all copies or substantial portions of the Software.
 
18
// 
 
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
25
// THE SOFTWARE.
 
26
using System;
 
27
using System.Collections.Generic;
 
28
using ICSharpCode.NRefactory.Semantics;
 
29
using System.Linq;
 
30
using ICSharpCode.NRefactory.CSharp.Resolver;
 
31
using ICSharpCode.NRefactory.CSharp.Analysis;
 
32
using System.Threading;
 
33
using ICSharpCode.NRefactory.TypeSystem;
 
34
using System.Threading.Tasks;
 
35
 
 
36
namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod
 
37
{
 
38
        [ContextAction("Extract method", Description = "Creates a new method out of selected text.")]
 
39
        public class ExtractMethodAction : ICodeActionProvider
 
40
        {
 
41
                public IEnumerable<CodeAction> GetActions(RefactoringContext context)
 
42
                {
 
43
                        if (!context.IsSomethingSelected)
 
44
                                yield break;
 
45
                        var selected = new List<AstNode>(context.GetSelectedNodes());
 
46
                        if (selected.Count == 0)
 
47
                                yield break;
 
48
                        
 
49
                        if (selected.Count == 1 && selected [0] is Expression) {
 
50
                                var codeAction = CreateFromExpression(context, (Expression)selected [0]);
 
51
                                if (codeAction == null)
 
52
                                        yield break;
 
53
                                yield return codeAction;
 
54
                        }
 
55
 
 
56
                        foreach (var node in selected) {
 
57
                                if (!(node is Statement) && !(node is Comment) && !(node is NewLineNode) && !(node is PreProcessorDirective))
 
58
                                        yield break;
 
59
                        }
 
60
                        var action = CreateFromStatements (context, new List<AstNode> (selected));
 
61
                        if (action != null)
 
62
                                yield return action;
 
63
                }
 
64
                
 
65
                CodeAction CreateFromExpression(RefactoringContext context, Expression expression)
 
66
                {
 
67
                        var resolveResult = context.Resolve(expression);
 
68
                        if (resolveResult.IsError)
 
69
                                return null;
 
70
                        
 
71
                        return new CodeAction(context.TranslateString("Extract method"), script => {
 
72
                                string methodName = "NewMethod";
 
73
                                var method = new MethodDeclaration {
 
74
                                        ReturnType = context.CreateShortType(resolveResult.Type),
 
75
                                        Name = methodName,
 
76
                                        Body = new BlockStatement {
 
77
                                                new ReturnStatement(expression.Clone())
 
78
                                        }
 
79
                                };
 
80
                                if (!StaticVisitor.UsesNotStaticMember(context, expression))
 
81
                                        method.Modifiers |= Modifiers.Static;
 
82
 
 
83
                                var usedVariables = VariableLookupVisitor.Analyze(context, expression);
 
84
                                
 
85
                                var inExtractedRegion = new VariableUsageAnalyzation (context, usedVariables);
 
86
 
 
87
                                usedVariables.Sort ((l, r) => l.Region.Begin.CompareTo (r.Region.Begin));
 
88
                                var target = new IdentifierExpression(methodName);
 
89
                                var invocation = new InvocationExpression(target);
 
90
                                foreach (var variable in usedVariables) {
 
91
                                        Expression argumentExpression = new IdentifierExpression(variable.Name); 
 
92
                                        
 
93
                                        var mod = ParameterModifier.None;
 
94
                                        if (inExtractedRegion.GetStatus (variable) == VariableState.Changed) {
 
95
                                                mod = ParameterModifier.Ref;
 
96
                                                argumentExpression = new DirectionExpression(FieldDirection.Ref, argumentExpression);
 
97
                                        }
 
98
                                        
 
99
                                        method.Parameters.Add(new ParameterDeclaration(context.CreateShortType(variable.Type), variable.Name, mod));
 
100
                                        invocation.Arguments.Add(argumentExpression);
 
101
                                }
 
102
 
 
103
                                var task = script.InsertWithCursor(context.TranslateString("Extract method"), Script.InsertPosition.Before, method);
 
104
 
 
105
                                Action<Task> replaceStatements = delegate {
 
106
                                        script.Replace(expression, invocation);
 
107
                                        script.Link(target, method.NameToken);
 
108
                                };
 
109
 
 
110
                                if (task.IsCompleted) {
 
111
                                        replaceStatements (null);
 
112
                                } else {
 
113
                                        task.ContinueWith (replaceStatements, TaskScheduler.FromCurrentSynchronizationContext ());
 
114
                                }
 
115
                        }, expression);
 
116
                }
 
117
                
 
118
                CodeAction CreateFromStatements(RefactoringContext context, List<AstNode> statements)
 
119
                {
 
120
                        if (!(statements [0].Parent is Statement))
 
121
                                return null;
 
122
                        
 
123
                        return new CodeAction(context.TranslateString("Extract method"), script => {
 
124
                                string methodName = "NewMethod";
 
125
                                var method = new MethodDeclaration() {
 
126
                                        ReturnType = new PrimitiveType("void"),
 
127
                                        Name = methodName,
 
128
                                        Body = new BlockStatement()
 
129
                                };
 
130
                                bool usesNonStaticMember = false;
 
131
                                foreach (var node in statements) {
 
132
                                        usesNonStaticMember |= StaticVisitor.UsesNotStaticMember(context, node);
 
133
                                        if (node is Statement) {
 
134
                                                method.Body.Add((Statement)node.Clone());
 
135
                                        } else {
 
136
                                                method.Body.AddChildUnsafe (node.Clone (), node.Role);
 
137
                                        }
 
138
                                }
 
139
                                if (!usesNonStaticMember)
 
140
                                        method.Modifiers |= Modifiers.Static;
 
141
                                
 
142
                                var target = new IdentifierExpression(methodName);
 
143
                                var invocation = new InvocationExpression(target);
 
144
                                
 
145
                                var usedVariables = VariableLookupVisitor.Analyze(context, statements);
 
146
                                
 
147
                                var inExtractedRegion = new VariableUsageAnalyzation (context, usedVariables);
 
148
                                var lastStatement = statements [statements.Count - 1];
 
149
                                
 
150
                                var stmt = statements [0].GetParent<BlockStatement>();
 
151
                                while (stmt.GetParent<BlockStatement> () != null) {
 
152
                                        stmt = stmt.GetParent<BlockStatement>();
 
153
                                }
 
154
                                
 
155
                                inExtractedRegion.SetAnalyzedRange(statements [0], lastStatement);
 
156
                                stmt.AcceptVisitor (inExtractedRegion);
 
157
                                
 
158
                                var beforeExtractedRegion = new VariableUsageAnalyzation (context, usedVariables);
 
159
                                beforeExtractedRegion.SetAnalyzedRange(statements [0].Parent, statements [0], true, false);
 
160
                                stmt.AcceptVisitor (beforeExtractedRegion);
 
161
                                
 
162
                                var afterExtractedRegion = new VariableUsageAnalyzation (context, usedVariables);
 
163
                                afterExtractedRegion.SetAnalyzedRange(lastStatement, stmt.Statements.Last(), false, true);
 
164
                                stmt.AcceptVisitor (afterExtractedRegion);
 
165
                                usedVariables.Sort ((l, r) => l.Region.Begin.CompareTo (r.Region.Begin));
 
166
 
 
167
                                IVariable generatedReturnVariable = null;
 
168
                                foreach (var variable in usedVariables) {
 
169
                                        if ((variable is IParameter) || beforeExtractedRegion.Has (variable) || !afterExtractedRegion.Has (variable))
 
170
                                                continue;
 
171
                                        generatedReturnVariable = variable;
 
172
                                        method.ReturnType = context.CreateShortType (variable.Type);
 
173
                                        method.Body.Add (new ReturnStatement (new IdentifierExpression (variable.Name)));
 
174
                                        break;
 
175
                                }
 
176
 
 
177
                                foreach (var variable in usedVariables) {
 
178
                                        if (!(variable is IParameter) && !beforeExtractedRegion.Has (variable) && !afterExtractedRegion.Has (variable))
 
179
                                                continue;
 
180
                                        if (variable == generatedReturnVariable)
 
181
                                                continue;
 
182
                                        Expression argumentExpression = new IdentifierExpression(variable.Name); 
 
183
                                        
 
184
                                        ParameterModifier mod = ParameterModifier.None;
 
185
                                        if (inExtractedRegion.GetStatus (variable) == VariableState.Changed) {
 
186
                                                if (beforeExtractedRegion.GetStatus (variable) == VariableState.None) {
 
187
                                                        mod = ParameterModifier.Out;
 
188
                                                        argumentExpression = new DirectionExpression(FieldDirection.Out, argumentExpression);
 
189
                                                } else {
 
190
                                                        mod = ParameterModifier.Ref;
 
191
                                                        argumentExpression = new DirectionExpression(FieldDirection.Ref, argumentExpression);
 
192
                                                }
 
193
                                        }
 
194
                                        
 
195
                                        method.Parameters.Add(new ParameterDeclaration(context.CreateShortType(variable.Type), variable.Name, mod));
 
196
                                        invocation.Arguments.Add(argumentExpression);
 
197
                                }
 
198
                                var task = script.InsertWithCursor(context.TranslateString("Extract method"), Script.InsertPosition.Before, method);
 
199
                                Action<Task> replaceStatements = delegate {
 
200
                                        foreach (var node in statements.Skip (1)) {
 
201
                                                if (node is NewLineNode)
 
202
                                                        continue;
 
203
                                                script.Remove(node);
 
204
                                        }
 
205
                                        foreach (var variable in usedVariables) {
 
206
                                                if ((variable is IParameter) || beforeExtractedRegion.Has (variable) || !afterExtractedRegion.Has (variable))
 
207
                                                        continue;
 
208
                                                if (variable == generatedReturnVariable)
 
209
                                                        continue;
 
210
                                                script.InsertBefore (statements [0], new VariableDeclarationStatement (context.CreateShortType(variable.Type), variable.Name));
 
211
                                        }
 
212
                                        AstNode invocationStatement;
 
213
 
 
214
                                        if (generatedReturnVariable != null) {
 
215
                                                invocationStatement = new VariableDeclarationStatement (new SimpleType ("var"), generatedReturnVariable.Name, invocation);
 
216
                                        } else {
 
217
                                                invocationStatement = new ExpressionStatement(invocation);
 
218
                                        }
 
219
                                        script.Replace(statements [0], invocationStatement);
 
220
 
 
221
 
 
222
                                        script.Link(target, method.NameToken);
 
223
                                };
 
224
 
 
225
                                if (task.IsCompleted) {
 
226
                                        replaceStatements (null);
 
227
                                } else {
 
228
                                        task.ContinueWith (replaceStatements, TaskScheduler.FromCurrentSynchronizationContext ());
 
229
                                }
 
230
                        }, statements.First ().StartLocation, statements.Last ().EndLocation);
 
231
                }
 
232
        }
 
233
}