|
1 | 1 | using System;
|
| 2 | +using System.Collections; |
2 | 3 | using System.Collections.Generic;
|
| 4 | +using System.Diagnostics; |
3 | 5 | using System.Linq;
|
4 | 6 | using Antlr4.Runtime;
|
5 | 7 | using Microsoft.Vbe.Interop;
|
6 | 8 | using Rubberduck.Common;
|
7 | 9 | using Rubberduck.Parsing;
|
| 10 | +using Rubberduck.Parsing.Grammar; |
8 | 11 | using Rubberduck.Parsing.Symbols;
|
9 | 12 | using Rubberduck.VBEditor;
|
10 | 13 | using Rubberduck.VBEditor.Extensions;
|
11 | 14 |
|
12 | 15 | namespace Rubberduck.Refactorings.ExtractMethod
|
13 | 16 | {
|
14 |
| - public class ExtractMethodModel |
| 17 | + |
| 18 | + public class ExtractMethodModel : IExtractMethodModel |
15 | 19 | {
|
16 |
| - public ExtractMethodModel(VBE vbe, IEnumerable<Declaration> declarations, QualifiedSelection selection) |
| 20 | + private const string NEW_METHOD = "NewMethod"; |
| 21 | + |
| 22 | + public ExtractMethodModel(List<IExtractMethodRule> emRules, IExtractedMethod extractedMethod) |
17 | 23 | {
|
18 |
| - var items = declarations.ToList(); |
| 24 | + _rules = emRules; |
| 25 | + _extractedMethod = extractedMethod; |
| 26 | + } |
19 | 27 |
|
20 |
| - _sourceMember = items.FindSelectedDeclaration(selection, DeclarationExtensions.ProcedureTypes, d => ((ParserRuleContext)d.Context.Parent).GetSelection()); |
21 |
| - if (_sourceMember == null) |
| 28 | + |
| 29 | + public void extract(IEnumerable<Declaration> declarations, QualifiedSelection selection, string selectedCode) |
| 30 | + { |
| 31 | + var items = declarations.ToList(); |
| 32 | + var sourceMember = items.FindSelectedDeclaration(selection, DeclarationExtensions.ProcedureTypes, d => ((ParserRuleContext)d.Context.Parent).GetSelection()); |
| 33 | + if (sourceMember == null) |
22 | 34 | {
|
23 | 35 | throw new InvalidOperationException("Invalid selection.");
|
24 | 36 | }
|
25 | 37 |
|
| 38 | + var inScopeDeclarations = items.Where(item => item.ParentScope == sourceMember.Scope).ToList(); |
| 39 | + |
| 40 | + _byref = new List<Declaration>(); |
| 41 | + _byval = new List<Declaration>(); |
| 42 | + _declarationsToMove = new List<Declaration>(); |
| 43 | + |
26 | 44 | _extractedMethod = new ExtractedMethod();
|
| 45 | + |
27 | 46 |
|
28 |
| - _selection = selection; |
29 |
| - _selectedCode = vbe.ActiveCodePane.CodeModule.GetLines(selection.Selection); |
| 47 | + var selectionToRemove = new List<Selection>(); |
| 48 | + var selectionStartLine = selection.Selection.StartLine; |
| 49 | + var selectionEndLine = selection.Selection.EndLine; |
30 | 50 |
|
31 |
| - var inScopeDeclarations = items.Where(item => item.ParentScope == _sourceMember.Scope).ToList(); |
| 51 | + var methodInsertLine = sourceMember.Context.Stop.Line + 1; |
| 52 | + _positionForNewMethod = new Selection(methodInsertLine, 1, methodInsertLine, 1); |
32 | 53 |
|
33 |
| - var inSelection = inScopeDeclarations.SelectMany(item => item.References) |
34 |
| - .Where(item => selection.Selection.Contains(item.Selection)) |
35 |
| - .ToList(); |
| 54 | + // https://github.com/rubberduck-vba/Rubberduck/wiki/Extract-Method-Refactoring-%3A-Workings---Determining-what-params-to-move |
| 55 | + foreach (var item in inScopeDeclarations) |
| 56 | + { |
| 57 | + var flags = new Byte(); |
| 58 | + |
| 59 | + foreach (var oRef in item.References) |
| 60 | + { |
| 61 | + foreach (var rule in _rules) |
| 62 | + { |
| 63 | + rule.setValidFlag(ref flags, oRef, selection.Selection); |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + //TODO: extract this to seperate class. |
| 68 | + if (flags < 4) { /*ignore the variable*/ } |
| 69 | + else if (flags < 12) |
| 70 | + _byref.Add(item); |
| 71 | + else if (flags == 12) |
| 72 | + _declarationsToMove.Add(item); |
| 73 | + else if (flags > 12) |
| 74 | + _byval.Add(item); |
36 | 75 |
|
37 |
| - var usedInSelection = new HashSet<Declaration>(inScopeDeclarations.Where(item => |
38 |
| - selection.Selection.Contains(item.Selection) || |
39 |
| - item.References.Any(reference => inSelection.Contains(reference)))); |
| 76 | + } |
40 | 77 |
|
41 |
| - var usedBeforeSelection = new HashSet<Declaration>(inScopeDeclarations.Where(item => |
42 |
| - item.Selection.StartLine < selection.Selection.StartLine || |
43 |
| - item.References.Any(reference => reference.Selection.StartLine < selection.Selection.StartLine))); |
| 78 | + _declarationsToMove.ForEach(d => selectionToRemove.Add(d.Selection)); |
| 79 | + selectionToRemove.Add(selection.Selection); |
44 | 80 |
|
45 |
| - var usedAfterSelection = new HashSet<Declaration>(inScopeDeclarations.Where(item => |
46 |
| - item.Selection.StartLine > selection.Selection.StartLine || |
47 |
| - item.References.Any(reference => reference.Selection.StartLine > selection.Selection.EndLine))); |
| 81 | + var methodCallPositionStartLine = selectionStartLine - selectionToRemove.Count(s => s.StartLine < selectionStartLine); |
| 82 | + _positionForMethodCall = new Selection(methodCallPositionStartLine, 1, methodCallPositionStartLine, 1); |
48 | 83 |
|
49 |
| - // identifiers used inside selection and before selection (or if it's a parameter) are candidates for parameters: |
50 |
| - var input = inScopeDeclarations.Where(item => |
51 |
| - usedInSelection.Contains(item) && (usedBeforeSelection.Contains(item) || item.DeclarationType == DeclarationType.Parameter)).ToList(); |
| 84 | + var methodParams = _byref.Select(dec => new ExtractedParameter(dec.AsTypeName, ExtractedParameter.PassedBy.ByRef, dec.IdentifierName)) |
| 85 | + .Union(_byval.Select(dec => new ExtractedParameter(dec.AsTypeName, ExtractedParameter.PassedBy.ByVal, dec.IdentifierName))); |
52 | 86 |
|
53 |
| - // identifiers used inside selection and after selection are candidates for return values: |
54 |
| - var output = inScopeDeclarations.Where(item => |
55 |
| - usedInSelection.Contains(item) && usedAfterSelection.Contains(item)) |
56 |
| - .ToList(); |
| 87 | + // iterate until we have a non-clashing method name. |
| 88 | + var newMethodName = NEW_METHOD; |
57 | 89 |
|
58 |
| - // identifiers used only inside and/or after selection are candidates for locals: |
59 |
| - _locals = inScopeDeclarations.Where(item => item.DeclarationType != DeclarationType.Parameter && ( |
60 |
| - item.References.All(reference => inSelection.Contains(reference)) |
61 |
| - || (usedAfterSelection.Contains(item) && (!usedBeforeSelection.Contains(item))))) |
62 |
| - .ToList(); |
| 90 | + var newMethodInc = 0; |
| 91 | + while (declarations.FirstOrDefault(d => |
| 92 | + DeclarationExtensions.ProcedureTypes.Contains(d.DeclarationType) |
| 93 | + && d.IdentifierName.Equals(newMethodName)) != null) |
| 94 | + { |
| 95 | + newMethodInc++; |
| 96 | + newMethodName = NEW_METHOD + newMethodInc; |
| 97 | + } |
63 | 98 |
|
64 |
| - // locals that are only used in selection are candidates for being moved into the new method: |
65 |
| - _declarationsToMove = _locals.Where(item => !usedAfterSelection.Contains(item)).ToList(); |
| 99 | + _extractedMethod.MethodName = newMethodName; |
| 100 | + _extractedMethod.ReturnValue = null; |
| 101 | + _extractedMethod.Accessibility = Accessibility.Private; |
| 102 | + _extractedMethod.SetReturnValue = false; |
| 103 | + _extractedMethod.Parameters = methodParams.ToList(); |
66 | 104 |
|
67 |
| - _output = output.Select(declaration => |
68 |
| - new ExtractedParameter(declaration.AsTypeName, ExtractedParameter.PassedBy.ByRef, declaration.IdentifierName)); |
| 105 | + _selection = selection; |
| 106 | + _selectedCode = selectedCode; |
| 107 | + _selectionToRemove = selectionToRemove.ToList(); |
69 | 108 |
|
70 |
| - _input = input.Where(declaration => !output.Contains(declaration)) |
71 |
| - .Select(declaration => |
72 |
| - new ExtractedParameter(declaration.AsTypeName, ExtractedParameter.PassedBy.ByVal, declaration.IdentifierName)); |
73 | 109 | }
|
74 | 110 |
|
75 |
| - private readonly Declaration _sourceMember; |
| 111 | + private List<Declaration> _byref; |
| 112 | + private List<Declaration> _byval; |
| 113 | + private List<Declaration> _moveIn; |
| 114 | + |
| 115 | + private Declaration _sourceMember; |
76 | 116 | public Declaration SourceMember { get { return _sourceMember; } }
|
77 | 117 |
|
78 |
| - private readonly QualifiedSelection _selection; |
| 118 | + private QualifiedSelection _selection; |
79 | 119 | public QualifiedSelection Selection { get { return _selection; } }
|
80 | 120 |
|
81 |
| - private readonly string _selectedCode; |
| 121 | + private string _selectedCode; |
82 | 122 | public string SelectedCode { get { return _selectedCode; } }
|
83 | 123 |
|
84 |
| - private readonly List<Declaration> _locals; |
85 |
| - public IEnumerable<Declaration> Locals { get {return _locals;} } |
| 124 | + private List<Declaration> _locals; |
| 125 | + public IEnumerable<Declaration> Locals { get { return _locals; } } |
86 | 126 |
|
87 |
| - private readonly IEnumerable<ExtractedParameter> _input; |
| 127 | + private IEnumerable<ExtractedParameter> _input; |
88 | 128 | public IEnumerable<ExtractedParameter> Inputs { get { return _input; } }
|
89 | 129 |
|
90 |
| - private readonly IEnumerable<ExtractedParameter> _output; |
91 |
| - public IEnumerable<ExtractedParameter> Outputs { get {return _output; } } |
| 130 | + private IEnumerable<ExtractedParameter> _output; |
| 131 | + public IEnumerable<ExtractedParameter> Outputs { get { return _output; } } |
92 | 132 |
|
93 |
| - private readonly List<Declaration> _declarationsToMove; |
| 133 | + private List<Declaration> _declarationsToMove; |
94 | 134 | public IEnumerable<Declaration> DeclarationsToMove { get { return _declarationsToMove; } }
|
95 | 135 |
|
96 |
| - private readonly ExtractedMethod _extractedMethod; |
97 |
| - public ExtractedMethod Method { get { return _extractedMethod; } } |
| 136 | + private IExtractedMethod _extractedMethod; |
| 137 | + |
| 138 | + private IEnumerable<IExtractMethodRule> _rules; |
| 139 | + |
| 140 | + public IExtractedMethod Method { get { return _extractedMethod; } } |
| 141 | + |
| 142 | + |
| 143 | + private Selection _positionForMethodCall; |
| 144 | + public Selection PositionForMethodCall { get { return _positionForMethodCall; } } |
| 145 | + |
| 146 | + public string NewMethodCall { get { return _extractedMethod.NewMethodCall(); } } |
| 147 | + |
| 148 | + private Selection _positionForNewMethod; |
| 149 | + public Selection PositionForNewMethod { get { return _positionForNewMethod; } } |
| 150 | + IEnumerable<Selection> _selectionToRemove; |
| 151 | + private List<IExtractMethodRule> emRules; |
| 152 | + |
| 153 | + public IEnumerable<Selection> SelectionToRemove { get { return _selectionToRemove; } } |
| 154 | + |
98 | 155 |
|
99 |
| - public class ExtractedMethod |
100 |
| - { |
101 |
| - public string MethodName { get; set; } |
102 |
| - public Accessibility Accessibility { get; set; } |
103 |
| - public bool SetReturnValue { get; set; } |
104 |
| - public ExtractedParameter ReturnValue { get; set; } |
105 |
| - public IEnumerable<ExtractedParameter> Parameters { get; set; } |
106 |
| - } |
107 | 156 | }
|
108 | 157 | }
|
0 commit comments