~ubuntu-branches/ubuntu/oneiric/monodevelop/oneiric

« back to all changes in this revision

Viewing changes to src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/ExtractMethod/ExtractMethodRefactoring.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2011-06-27 17:03:13 UTC
  • mto: (1.8.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 54.
  • Revision ID: james.westby@ubuntu.com-20110627170313-6cvz3s19x6e9hqe9
ImportĀ upstreamĀ versionĀ 2.5.92+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// 
 
2
// ExtractMethod.cs
 
3
//  
 
4
// Author:
 
5
//       Mike KrĆ¼ger <mkrueger@novell.com>
 
6
// 
 
7
// Copyright (c) 2009 Novell, Inc (http://www.novell.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
 
 
27
using System;
 
28
using System.Linq;
 
29
using System.Collections.Generic;
 
30
using System.IO;
 
31
 
 
32
using MonoDevelop.Ide.Gui;
 
33
using MonoDevelop.Projects.Dom;
 
34
using MonoDevelop.Projects.Dom.Parser;
 
35
using MonoDevelop.Core;
 
36
using Mono.TextEditor;
 
37
using MonoDevelop.Ide;
 
38
using System.Text;
 
39
using Mono.TextEditor.PopupWindow;
 
40
using MonoDevelop.Refactoring;
 
41
using MonoDevelop.CSharp.Parser;
 
42
using MonoDevelop.CSharp.Ast;
 
43
using MonoDevelop.Projects.Text;
 
44
using MonoDevelop.Projects.Dom.Output;
 
45
using MonoDevelop.CSharp.Resolver;
 
46
using MonoDevelop.CSharp.Formatting;
 
47
 
 
48
namespace MonoDevelop.CSharp.Refactoring.ExtractMethod
 
49
{
 
50
        public class ExtractMethodRefactoring : RefactoringOperation
 
51
        {
 
52
                public override string AccelKey {
 
53
                        get {
 
54
                                var cmdInfo = IdeApp.CommandService.GetCommandInfo (RefactoryCommands.ExtractMethod);
 
55
                                if (cmdInfo != null && cmdInfo.AccelKey != null)
 
56
                                        return cmdInfo.AccelKey.Replace ("dead_circumflex", "^");
 
57
                                return null;
 
58
                        }
 
59
                }
 
60
                
 
61
                public ExtractMethodRefactoring ()
 
62
                {
 
63
                        Name = "Extract Method";
 
64
                }
 
65
                
 
66
                public override bool IsValid (RefactoringOptions options)
 
67
                {
 
68
//                      if (options.SelectedItem != null)
 
69
//                              return false;
 
70
                        var buffer = options.Document.Editor;
 
71
                        if (buffer.Document.MimeType != CSharpFormatter.MimeType)
 
72
                                return false;
 
73
                        if (buffer.IsSomethingSelected) {
 
74
                                ParsedDocument doc = options.ParseDocument ();
 
75
                                if (doc != null && doc.CompilationUnit != null) {
 
76
                                        var member = doc.CompilationUnit.GetMemberAt (buffer.Caret.Line, buffer.Caret.Column);
 
77
                                        if (member == null)
 
78
                                                return false;
 
79
                                        if (!member.BodyRegion.Contains (buffer.Caret.Line, buffer.Caret.Column))
 
80
                                                return false;
 
81
                                        return true;
 
82
                                }
 
83
                        }
 
84
                        return false;
 
85
                }
 
86
                
 
87
                public override string GetMenuDescription (RefactoringOptions options)
 
88
                {
 
89
                        return GettextCatalog.GetString ("_Extract Method...");
 
90
                }
 
91
                
 
92
                public override void Run (RefactoringOptions options)
 
93
                {
 
94
                        ExtractMethodParameters param = CreateParameters (options);
 
95
                        if (param == null)
 
96
                                return;
 
97
                        if (!Analyze (options, param, false)) {
 
98
                                MessageService.ShowError (GettextCatalog.GetString ("Invalid selection for method extraction."));
 
99
                                return;
 
100
                        }
 
101
                        MessageService.ShowCustomDialog (new ExtractMethodDialog (options, this, param));
 
102
                }
 
103
                
 
104
                public ExtractMethodParameters CreateParameters (RefactoringOptions options)
 
105
                {
 
106
                        var buffer = options.Document.Editor;
 
107
                        
 
108
                        if (!buffer.IsSomethingSelected)
 
109
                                return null;
 
110
 
 
111
                        ParsedDocument doc = options.ParseDocument ();
 
112
                        if (doc == null || doc.CompilationUnit == null)
 
113
                                return null;
 
114
 
 
115
                        IMember member = doc.CompilationUnit.GetMemberAt (buffer.Caret.Line, buffer.Caret.Column);
 
116
                        if (member == null)
 
117
                                return null;
 
118
                        
 
119
                        ExtractMethodParameters param = new ExtractMethodParameters () {
 
120
                                DeclaringMember = member,
 
121
                                Location = new DomLocation (buffer.Caret.Line, buffer.Caret.Column)
 
122
                        };
 
123
                        Analyze (options, param, true);
 
124
                        return param;
 
125
                }
 
126
                
 
127
                public class ExtractMethodParameters 
 
128
                {
 
129
                        public IMember DeclaringMember {
 
130
                                get;
 
131
                                set;
 
132
                        }
 
133
                                
 
134
                        public string Name {
 
135
                                get;
 
136
                                set;
 
137
                        }
 
138
                        
 
139
                        public string Text {
 
140
                                get;
 
141
                                set;
 
142
                        }
 
143
                        
 
144
                        public bool GenerateComment {
 
145
                                get;
 
146
                                set;
 
147
                        }
 
148
                        
 
149
                        public bool ReferencesMember {
 
150
                                get;
 
151
                                set;
 
152
                        }
 
153
                        
 
154
                        public InsertionPoint InsertionPoint {
 
155
                                get;
 
156
                                set;
 
157
                        }
 
158
                        
 
159
                        public DomLocation Location {
 
160
                                get;
 
161
                                set;
 
162
                        }
 
163
                        
 
164
                        public Modifiers Modifiers {
 
165
                                get;
 
166
                                set;
 
167
                        }
 
168
                        
 
169
                        public List<VariableDescriptor> Variables {
 
170
                                get;
 
171
                                set;
 
172
                        }
 
173
                        
 
174
                        public List<AstNode> Nodes {
 
175
                                get;
 
176
                                set;
 
177
                        }
 
178
                        
 
179
                        public int StartOffset {
 
180
                                get;
 
181
                                set;
 
182
                        }
 
183
                        
 
184
                        public int EndOffset {
 
185
                                get;
 
186
                                set;
 
187
                        }
 
188
                        
 
189
        
 
190
                        /// <summary>
 
191
                        /// The type of the expression, if the text is an expression, otherwise null.
 
192
                        /// </summary>
 
193
                        public IReturnType ExpressionType {
 
194
                                get;
 
195
                                set;
 
196
                        }
 
197
                        
 
198
                        public bool OneChangedVariable {
 
199
                                get;
 
200
                                set;
 
201
                        }
 
202
                        
 
203
                        List<VariableDescriptor> parameters = new List<VariableDescriptor> ();
 
204
                        public List<VariableDescriptor> Parameters {
 
205
                                get {
 
206
                                        return parameters;
 
207
                                }
 
208
                        }
 
209
                }
 
210
                
 
211
                static string GetIndent (string text)
 
212
                {
 
213
                        Mono.TextEditor.Document doc = new Mono.TextEditor.Document ();
 
214
                        doc.Text = text;
 
215
                        string result = null;
 
216
                        for (int i = 1; i < doc.LineCount; i++) {
 
217
                                string lineIndent = doc.GetLineIndent (i);
 
218
                                if (doc.GetLine (i).EditableLength == lineIndent.Length)
 
219
                                        continue;
 
220
                                if (result == null || lineIndent.Length < result.Length)
 
221
                                        result = lineIndent;
 
222
                        }
 
223
                        return result ?? "";
 
224
                }
 
225
                
 
226
                static string RemoveIndent (string text, string indent)
 
227
                {
 
228
                        Mono.TextEditor.Document doc = new Mono.TextEditor.Document ();
 
229
                        doc.Text = text;
 
230
                        StringBuilder result = new StringBuilder ();
 
231
                        bool firstLine = true;
 
232
                        foreach (LineSegment line in doc.Lines) {
 
233
                                string curLineIndent = line.GetIndentation (doc);
 
234
                                if (firstLine && curLineIndent.Length == line.EditableLength)
 
235
                                        continue;
 
236
                                firstLine = false;
 
237
                                int offset = Math.Min (curLineIndent.Length, indent.Length);
 
238
                                result.Append (doc.GetTextBetween (line.Offset + offset, line.EndOffset));
 
239
                        }
 
240
                        return result.ToString ();
 
241
                }
 
242
                
 
243
                static string AddIndent (string text, string indent)
 
244
                {
 
245
                        Mono.TextEditor.Document doc = new Mono.TextEditor.Document ();
 
246
                        doc.Text = text;
 
247
                        StringBuilder result = new StringBuilder ();
 
248
                        foreach (LineSegment line in doc.Lines) {
 
249
                                if (result.Length > 0)
 
250
                                        result.Append (indent);
 
251
                                result.Append (doc.GetTextAt (line));
 
252
                        }
 
253
                        return result.ToString ();
 
254
                }
 
255
                
 
256
                bool Analyze (RefactoringOptions options, ExtractMethodParameters param, bool fillParameter)
 
257
                {
 
258
                        var data = options.GetTextEditorData ();
 
259
                        var parser = new CSharpParser ();
 
260
                        var unit = parser.Parse (data);
 
261
                        var resolver = options.GetResolver ();
 
262
                        if (unit == null)
 
263
                                return false;
 
264
                        var selectionRange = data.SelectionRange;
 
265
                        var startOffset = selectionRange.Offset;
 
266
                        while (startOffset + 1 < data.Length && char.IsWhiteSpace (data.GetCharAt (startOffset + 1)))
 
267
                                startOffset++;
 
268
                        var endOffset = selectionRange.EndOffset;
 
269
                        while (startOffset < endOffset && endOffset - 1 > 0 && char.IsWhiteSpace (data.GetCharAt (endOffset - 1)))
 
270
                                endOffset--;
 
271
                        if (startOffset >= endOffset)
 
272
                                return false;
 
273
                        
 
274
                        var endLocation = data.OffsetToLocation (endOffset);
 
275
                        var startLocation = data.OffsetToLocation (startOffset);
 
276
                        param.StartOffset = startOffset;
 
277
                        param.EndOffset = endOffset;
 
278
                        param.Nodes = new List<AstNode> (unit.GetNodesBetween (startLocation.Line, startLocation.Column, endLocation.Line, endLocation.Column));
 
279
                        
 
280
                        string text = options.Document.Editor.GetTextBetween (startLocation, endLocation);
 
281
                        
 
282
                        param.Text = RemoveIndent (text, GetIndent (data.GetTextBetween (data.GetLine (startLocation.Line).Offset, data.GetLine (endLocation.Line).EndOffset))).TrimEnd ('\n', '\r');
 
283
                        VariableLookupVisitor visitor = new VariableLookupVisitor (resolver, param.Location);
 
284
                        visitor.MemberLocation = param.DeclaringMember.Location;
 
285
                        visitor.CutRegion = new DomRegion (startLocation.Line, startLocation.Column, endLocation.Line, endLocation.Column);
 
286
                        if (fillParameter) {
 
287
                                unit.AcceptVisitor (visitor, null);
 
288
                                if (param.Nodes != null && (param.Nodes.Count == 1 && param.Nodes [0].NodeType == NodeType.Expression)) {
 
289
                                        ResolveResult resolveResult = resolver.Resolve (new ExpressionResult ("(" + text + ")"), param.Location);
 
290
                                        if (resolveResult != null && resolveResult.ResolvedType != null)
 
291
                                                param.ExpressionType = resolveResult.ResolvedType;
 
292
                                }
 
293
                                
 
294
                                foreach (VariableDescriptor varDescr in visitor.VariableList.Where (v => !v.IsDefinedInsideCutRegion && (v.UsedInCutRegion || v.IsChangedInsideCutRegion || v.UsedAfterCutRegion && v.IsDefinedInsideCutRegion))) {
 
295
                                        param.Parameters.Add (varDescr);
 
296
                                }
 
297
                        
 
298
                                param.Variables = new List<VariableDescriptor> (visitor.Variables.Values);
 
299
                                param.ReferencesMember = visitor.ReferencesMember;
 
300
                                
 
301
                                param.OneChangedVariable = param.Variables.Count (p => p.IsDefinedInsideCutRegion && p.UsedAfterCutRegion) == 1;
 
302
                                if (param.OneChangedVariable)
 
303
                                        param.ExpressionType = param.Variables.First (p => p.IsDefinedInsideCutRegion && p.UsedAfterCutRegion).ReturnType;
 
304
                                /*
 
305
                                        foreach (VariableDescriptor varDescr in visitor.VariableList.Where (v => !v.IsDefined && param.Variables.Contains (v))) {
 
306
                                        if (param.Parameters.Contains (varDescr))
 
307
                                                continue;
 
308
                                        if (startLocation <= varDescr.Location && varDescr.Location < endLocation)
 
309
                                                continue;
 
310
                                        param.Parameters.Add (varDescr);
 
311
                                }
 
312
                                
 
313
                                
 
314
                                param.ChangedVariables = new HashSet<string> (visitor.Variables.Values.Where (v => v.GetsChanged).Select (v => v.Name));
 
315
                                */
 
316
                                // analyze the variables outside of the selected text
 
317
                                IMember member = param.DeclaringMember;
 
318
                        
 
319
                                int bodyStartOffset = data.Document.LocationToOffset (member.BodyRegion.Start.Line, member.BodyRegion.Start.Column);
 
320
                                int bodyEndOffset = data.Document.LocationToOffset (member.BodyRegion.End.Line, member.BodyRegion.End.Column);
 
321
                                if (startOffset < bodyStartOffset || bodyEndOffset < endOffset)
 
322
                                        return false;
 
323
                                text = data.Document.GetTextBetween (bodyStartOffset, startOffset) + data.Document.GetTextBetween (endOffset, bodyEndOffset);
 
324
                                //                              ICSharpCode.NRefactory.Ast.INode parsedNode = provider.ParseText (text);
 
325
                                //                              visitor = new VariableLookupVisitor (resolver, param.Location);
 
326
                                //                              visitor.CutRegion = new DomRegion (data.MainSelection.MinLine, data.MainSelection.MaxLine);
 
327
                                //                              visitor.MemberLocation = new Location (param.DeclaringMember.Location.Column, param.DeclaringMember.Location.Line);
 
328
                                //                              if (parsedNode != null)
 
329
                                //                                      parsedNode.AcceptVisitor (visitor, null);
 
330
                                
 
331
                                
 
332
                                /*      
 
333
                                param.VariablesOutside = new Dictionary<string, VariableDescriptor> ();
 
334
                                foreach (var pair in visitor.Variables) {
 
335
                                        if (startLocation < pair.Value.Location || endLocation >= pair.Value.Location) {
 
336
                                                param.VariablesOutside.Add (pair.Key, pair.Value);
 
337
                                        }
 
338
                                }
 
339
                                param.OutsideVariableList = new List<VariableDescriptor> ();
 
340
                                foreach (var v in visitor.VariableList) {
 
341
                                        if (startLocation < v.Location || endLocation >= v.Location)
 
342
                                                param.OutsideVariableList.Add (v);
 
343
                                }
 
344
                                
 
345
                                param.ChangedVariablesUsedOutside = new List<VariableDescriptor> (param.Variables.Where (v => v.GetsChanged && param.VariablesOutside.ContainsKey (v.Name)));
 
346
                                param.OneChangedVariable = param.Nodes.Count == 1 && param.Nodes[0] is BlockStatement;
 
347
                                if (param.OneChangedVariable) 
 
348
                                        param.OneChangedVariable = param.ChangedVariablesUsedOutside.Count == 1;
 
349
                                
 
350
                                param.VariablesToGenerate = new List<VariableDescriptor> (param.ChangedVariablesUsedOutside.Where (v => v.IsDefined));
 
351
                                foreach (VariableDescriptor var in param.VariablesToGenerate) {
 
352
                                        param.Parameters.Add (var);
 
353
                                }
 
354
                                if (param.OneChangedVariable) {
 
355
                                        param.VariablesToDefine = new List<VariableDescriptor> (param.Parameters.Where (var => !var.InitialValueUsed));
 
356
                                        param.VariablesToDefine.ForEach (var => param.Parameters.Remove (var));
 
357
                                } else {
 
358
                                        param.VariablesToDefine = new List<VariableDescriptor> ();
 
359
                                }*/
 
360
                        }
 
361
                        
 
362
                        return true;
 
363
                }
 
364
                
 
365
                static string GenerateMethodCall (RefactoringOptions options, ExtractMethodParameters param)
 
366
                {
 
367
//                      var data = options.GetTextEditorData ();
 
368
                        StringBuilder sb = new StringBuilder ();
 
369
                        
 
370
        /*              LineSegment line = data.Document.GetLine (Math.Max (0, data.Document.OffsetToLineNumber (data.SelectionRange.Offset) - 1));
 
371
                        if (param.VariablesToGenerate != null && param.VariablesToGenerate.Count > 0) {
 
372
                                string indent = options.GetWhitespaces (line.Offset);
 
373
                                sb.Append (Environment.NewLine + indent);
 
374
                                foreach (VariableDescriptor var in param.VariablesToGenerate) {
 
375
                                        var returnType = options.ShortenTypeName (var.ReturnType);
 
376
                                        sb.Append (returnType.ToInvariantString ());
 
377
                                        sb.Append (" ");
 
378
                                        sb.Append (var.Name);
 
379
                                        sb.AppendLine (";");
 
380
                                        sb.Append (indent);
 
381
                                }
 
382
                        }*/
 
383
                        if (param.OneChangedVariable) {
 
384
                                var resultVariable = param.Variables.First (p => p.IsDefinedInsideCutRegion && p.UsedAfterCutRegion);
 
385
                                if (resultVariable.IsDefinedInsideCutRegion) {
 
386
                                        var s = resultVariable.Declaration.Type.StartLocation;
 
387
                                        var e = resultVariable.Declaration.Type.EndLocation;
 
388
                                        sb.Append (options.Document.Editor.GetTextBetween (s.Line, s.Column, e.Line, e.Column) + " ");
 
389
                                }
 
390
                                
 
391
                                sb.Append (resultVariable.Name);
 
392
                                sb.Append (" = ");
 
393
                        }
 
394
                        sb.Append (param.Name);
 
395
                        sb.Append (" "); // TODO: respect formatting
 
396
                        sb.Append ("(");
 
397
                        bool first = true;
 
398
                        foreach (VariableDescriptor var in param.Parameters) {
 
399
                                if (param.OneChangedVariable && var.UsedAfterCutRegion && !var.UsedInCutRegion)
 
400
                                        continue;
 
401
                                if (first) {
 
402
                                        first = false;
 
403
                                } else {
 
404
                                        sb.Append (", "); // TODO: respect formatting
 
405
                                }
 
406
                                if (!param.OneChangedVariable) {
 
407
                                        if (!var.IsDefinedInsideCutRegion && var.IsChangedInsideCutRegion) {
 
408
                                                sb.Append (var.UsedBeforeCutRegion ? "ref " : "out ");
 
409
                                        }
 
410
                                }
 
411
                                sb.Append (var.Name);
 
412
                        }
 
413
                        sb.Append (")");
 
414
                        if (param.Nodes != null && (param.Nodes.Count > 1 || param.Nodes.Count == 1 && param.Nodes[0].NodeType != NodeType.Expression)) 
 
415
                                sb.Append (";");
 
416
                        return sb.ToString ();
 
417
                }
 
418
                
 
419
                static DomMethod GenerateMethodStub (RefactoringOptions options, ExtractMethodParameters param)
 
420
                {
 
421
                        DomMethod result = new DomMethod ();
 
422
                        result.Name = param.Name;
 
423
                        result.ReturnType = param.ExpressionType ?? DomReturnType.Void;
 
424
                        result.Modifiers = param.Modifiers;
 
425
                        if (!param.ReferencesMember)
 
426
                                result.Modifiers |= Modifiers.Static;
 
427
                        
 
428
                        if (param.Parameters == null)
 
429
                                return result;
 
430
                        foreach (var p in param.Parameters) {
 
431
                                if (param.OneChangedVariable && p.UsedAfterCutRegion && !p.UsedInCutRegion)
 
432
                                        continue;
 
433
                                var newParameter = new DomParameter ();
 
434
                                newParameter.Name = p.Name;
 
435
                                newParameter.ReturnType = p.ReturnType;
 
436
                                
 
437
                                if (!param.OneChangedVariable) {
 
438
                                        if (!p.IsDefinedInsideCutRegion && p.IsChangedInsideCutRegion) {
 
439
                                                newParameter.ParameterModifiers = p.UsedBeforeCutRegion ? ParameterModifiers.Ref : ParameterModifiers.Out;
 
440
                                        }
 
441
                                }
 
442
                                result.Add (newParameter);
 
443
                        }
 
444
                        return result;
 
445
                }
 
446
                
 
447
                static string GenerateMethodDeclaration (RefactoringOptions options, ExtractMethodParameters param)
 
448
                {
 
449
                        StringBuilder methodText = new StringBuilder ();
 
450
                        string indent = options.GetIndent (param.DeclaringMember);
 
451
                        if (param.InsertionPoint != null) {
 
452
                                switch (param.InsertionPoint.LineBefore) {
 
453
                                case NewLineInsertion.Eol:
 
454
                                        methodText.AppendLine ();
 
455
                                        break;
 
456
                                case NewLineInsertion.BlankLine:
 
457
                                        methodText.Append (indent);
 
458
                                        methodText.AppendLine ();
 
459
                                        break;
 
460
                                }
 
461
                        } else {
 
462
                                methodText.AppendLine ();
 
463
                                methodText.Append (indent);
 
464
                                methodText.AppendLine ();
 
465
                        }
 
466
                        var codeGenerator = new CSharpCodeGenerator () {
 
467
                                UseSpaceIndent = options.Document.Editor.Options.TabsToSpaces,
 
468
                                EolMarker = options.Document.Editor.EolMarker,
 
469
                                TabSize = options.Document.Editor.Options.TabSize
 
470
                        };
 
471
                        
 
472
                        var newMethod = GenerateMethodStub (options, param);
 
473
                        IType callingType = null;
 
474
                        var cu = options.Document.CompilationUnit;
 
475
                        if (cu != null)
 
476
                                callingType = newMethod.DeclaringType = options.Document.CompilationUnit.GetTypeAt (options.Document.Editor.Caret.Line, options.Document.Editor.Caret.Column);
 
477
                                
 
478
                        var createdMethod = codeGenerator.CreateMemberImplementation (callingType, newMethod, false);
 
479
 
 
480
                        if (param.GenerateComment && DocGenerator.Instance != null)
 
481
                                methodText.AppendLine (DocGenerator.Instance.GenerateDocumentation (newMethod, indent + "/// "));
 
482
                        string code = createdMethod.Code;
 
483
                        int idx1 = code.LastIndexOf ("throw");
 
484
                        int idx2 = code.LastIndexOf (";");
 
485
                        methodText.Append (code.Substring (0, idx1));
 
486
 
 
487
                        if (param.Nodes != null && (param.Nodes.Count == 1 && param.Nodes[0].NodeType == NodeType.Expression)) {
 
488
                                methodText.Append ("return ");
 
489
                                methodText.Append (param.Text.Trim ());
 
490
                                methodText.Append (";");
 
491
                        } else {
 
492
                                StringBuilder text = new StringBuilder ();
 
493
                                if (param.OneChangedVariable) {
 
494
                                        var par = param.Variables.First (p => p.IsDefinedInsideCutRegion && p.UsedAfterCutRegion);
 
495
                                        if (!par.UsedInCutRegion) {
 
496
                                                
 
497
                                                text.Append (new CSharpAmbience ().GetString (par.ReturnType, OutputFlags.ClassBrowserEntries));
 
498
                                                text.Append (" ");
 
499
                                                text.Append (par.Name);
 
500
                                                text.AppendLine (";");
 
501
                                        }
 
502
                                }
 
503
                                text.Append (param.Text);
 
504
                                if (param.OneChangedVariable) {
 
505
                                        text.AppendLine ();
 
506
                                        text.Append ("return ");
 
507
                                        text.Append (param.Variables.First (p => p.IsDefinedInsideCutRegion && p.UsedAfterCutRegion).Name);
 
508
                                        text.Append (";");
 
509
                                }
 
510
                                methodText.Append (AddIndent (text.ToString (), indent + "\t"));
 
511
                        }
 
512
 
 
513
                        methodText.Append (code.Substring (idx2 + 1));
 
514
                        if (param.InsertionPoint != null) {
 
515
                                switch (param.InsertionPoint.LineAfter) {
 
516
                                case NewLineInsertion.Eol:
 
517
                                        methodText.AppendLine ();
 
518
                                        break;
 
519
                                case NewLineInsertion.BlankLine:
 
520
                                        methodText.AppendLine ();
 
521
                                        methodText.Append (indent);
 
522
                                        methodText.AppendLine ();
 
523
                                        break;
 
524
                                case NewLineInsertion.None:
 
525
                                        methodText.AppendLine ();
 
526
                                        break;
 
527
                                }
 
528
                        } else {
 
529
                                methodText.AppendLine ();
 
530
                                methodText.Append (indent);
 
531
                                methodText.AppendLine ();
 
532
                        }
 
533
                        return methodText.ToString ();
 
534
                }
 
535
                
 
536
                public override List<Change> PerformChanges (RefactoringOptions options, object prop)
 
537
                {
 
538
                        List<Change> result = new List<Change> ();
 
539
                        ExtractMethodParameters param = (ExtractMethodParameters)prop;
 
540
                        var data = options.GetTextEditorData ();
 
541
                //      IResolver resolver = options.GetResolver ();
 
542
                        
 
543
                        TextReplaceChange replacement = new TextReplaceChange ();
 
544
                        replacement.Description = string.Format (GettextCatalog.GetString ("Substitute selected statement(s) with call to {0}"), param.Name);
 
545
                        replacement.FileName = options.Document.FileName;
 
546
                        replacement.Offset = param.StartOffset;
 
547
                        replacement.RemovedChars = param.EndOffset - param.StartOffset;
 
548
                        replacement.MoveCaretToReplace = true;
 
549
                        replacement.InsertedText = GenerateMethodCall (options, param);
 
550
                        result.Add (replacement);
 
551
                        
 
552
                        TextReplaceChange insertNewMethod = new TextReplaceChange ();
 
553
                        insertNewMethod.FileName = options.Document.FileName;
 
554
                        insertNewMethod.Description = string.Format (GettextCatalog.GetString ("Create new method {0} from selected statement(s)"), param.Name);
 
555
                        var insertionPoint = param.InsertionPoint;
 
556
                        if (insertionPoint == null) {
 
557
                                var points = CodeGenerationService.GetInsertionPoints (options.Document, param.DeclaringMember.DeclaringType);
 
558
                                insertionPoint = points.LastOrDefault (p => p.Location.Line < param.DeclaringMember.Location.Line);
 
559
                                if (insertionPoint == null)
 
560
                                        insertionPoint = points.FirstOrDefault ();
 
561
                        }
 
562
                        
 
563
                        insertNewMethod.RemovedChars = 0; //insertionPoint.LineBefore == NewLineInsertion.Eol ? 0 : insertionPoint.Location.Column - 1;
 
564
                        insertNewMethod.Offset = data.Document.LocationToOffset (insertionPoint.Location) - insertNewMethod.RemovedChars;
 
565
                        insertNewMethod.InsertedText = GenerateMethodDeclaration (options, param);
 
566
                        result.Add (insertNewMethod);
 
567
                        /*
 
568
                        
 
569
                        ExtractMethodAstTransformer transformer = new ExtractMethodAstTransformer (param.VariablesToGenerate);
 
570
                        node.AcceptVisitor (transformer, null);
 
571
                        if (!param.OneChangedVariable && node is Expression) {
 
572
                                ResolveResult resolveResult = resolver.Resolve (new ExpressionResult ("(" + provider.OutputNode (options.Dom, node) + ")"), new DomLocation (options.Document.Editor.Caret.Line, options.Document.Editor.Caret.Column));
 
573
                                if (resolveResult.ResolvedType != null)
 
574
                                        returnType = options.ShortenTypeName (resolveResult.ResolvedType).ConvertToTypeReference ();
 
575
                        }
 
576
                        
 
577
                        MethodDeclaration methodDecl = new MethodDeclaration ();
 
578
                        methodDecl.Name = param.Name;
 
579
                        methodDecl.Modifier = param.Modifiers;
 
580
                        methodDecl.TypeReference = returnType;
 
581
                        
 
582
                        
 
583
                        if (node is BlockStatement) {
 
584
                                methodDecl.Body = new BlockStatement ();
 
585
                                methodDecl.Body.AddChild (new EmptyStatement ());
 
586
                                if (param.OneChangedVariable)
 
587
                                        methodDecl.Body.AddChild (new ReturnStatement (new IdentifierExpression (param.ChangedVariables.First ())));
 
588
                        } else if (node is Expression) {
 
589
                                methodDecl.Body = new BlockStatement ();
 
590
                                methodDecl.Body.AddChild (new ReturnStatement (node as Expression));
 
591
                        }
 
592
                        
 
593
                        foreach (VariableDescriptor var in param.VariablesToDefine) {
 
594
                                BlockStatement block = methodDecl.Body;
 
595
                                LocalVariableDeclaration varDecl = new LocalVariableDeclaration (options.ShortenTypeName (var.ReturnType).ConvertToTypeReference ());
 
596
                                varDecl.Variables.Add (new VariableDeclaration (var.Name));
 
597
                                block.Children.Insert (0, varDecl);
 
598
                        }
 
599
                        
 
600
                        
 
601
                        
 
602
                        string indent = options.GetIndent (param.DeclaringMember);
 
603
                        StringBuilder methodText = new StringBuilder ();
 
604
                        switch (param.InsertionPoint.LineBefore) {
 
605
                        case NewLineInsertion.Eol:
 
606
                                methodText.AppendLine ();
 
607
                                break;
 
608
                        case NewLineInsertion.BlankLine:
 
609
                                methodText.Append (indent);
 
610
                                methodText.AppendLine ();
 
611
                                break;
 
612
                        }
 
613
                        if (param.GenerateComment) {
 
614
                                methodText.Append (indent);
 
615
                                methodText.AppendLine ("/// <summary>");
 
616
                                methodText.Append (indent);
 
617
                                methodText.AppendLine ("/// TODO: write a comment.");
 
618
                                methodText.Append (indent);
 
619
                                methodText.AppendLine ("/// </summary>");
 
620
                                Ambience ambience = AmbienceService.GetAmbienceForFile (options.Document.FileName);
 
621
                                foreach (ParameterDeclarationExpression pde in methodDecl.Parameters) {
 
622
                                        methodText.Append (indent);
 
623
                                        methodText.Append ("/// <param name=\"");
 
624
                                        methodText.Append (pde.ParameterName);
 
625
                                        methodText.Append ("\"> A ");
 
626
                                        methodText.Append (ambience.GetString (pde.TypeReference.ConvertToReturnType (), OutputFlags.IncludeGenerics | OutputFlags.UseFullName));
 
627
                                        methodText.Append (" </param>");
 
628
                                        methodText.AppendLine ();
 
629
                                }
 
630
                                if (methodDecl.TypeReference.Type != "System.Void") {
 
631
                                        methodText.Append (indent);
 
632
                                        methodText.AppendLine ("/// <returns>");
 
633
                                        methodText.Append (indent);
 
634
                                        methodText.Append ("/// A ");
 
635
                                        methodText.AppendLine (ambience.GetString (methodDecl.TypeReference.ConvertToReturnType (), OutputFlags.IncludeGenerics | OutputFlags.UseFullName));
 
636
                                        methodText.Append (indent);
 
637
                                        methodText.AppendLine ("/// </returns>");
 
638
                                }
 
639
                        }
 
640
                        
 
641
                        methodText.Append (indent);
 
642
                        
 
643
                        if (node is BlockStatement) {
 
644
                                string text = provider.OutputNode (options.Dom, methodDecl, indent).Trim ();
 
645
                                int emptyStatementMarker = text.LastIndexOf (';');
 
646
                                if (param.OneChangedVariable)
 
647
                                        emptyStatementMarker = text.LastIndexOf (';', emptyStatementMarker - 1);
 
648
                                StringBuilder sb = new StringBuilder ();
 
649
                                sb.Append (text.Substring (0, emptyStatementMarker));
 
650
                                sb.Append (AddIndent (param.Text, indent + "\t"));
 
651
                                sb.Append (text.Substring (emptyStatementMarker + 1));
 
652
                                
 
653
                                methodText.Append (sb.ToString ());
 
654
                        } else {
 
655
                                methodText.Append (provider.OutputNode (options.Dom, methodDecl, options.GetIndent (param.DeclaringMember)).Trim ());
 
656
                        }
 
657
                        
 
658
                         */
 
659
                        return result;
 
660
                }
 
661
        }
 
662
}