Skip to content

Commit 1821f37

Browse files
committed
Implement Interface
1 parent 0e6648b commit 1821f37

File tree

11 files changed

+381
-11
lines changed

11 files changed

+381
-11
lines changed

RetailCoder.VBE/Common/DeclarationExtensions.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Diagnostics;
4+
using System.Diagnostics.CodeAnalysis;
45
using System.Linq;
56
using System.Windows.Media.Imaging;
67
using Microsoft.Vbe.Interop;
78
using Rubberduck.Annotations;
9+
using Rubberduck.Parsing;
810
using Rubberduck.Parsing.Grammar;
911
using Rubberduck.Parsing.Symbols;
1012
using Rubberduck.VBEditor;
@@ -441,5 +443,34 @@ public static Declaration FindVariable(this IEnumerable<Declaration> declaration
441443
}
442444
return null;
443445
}
446+
447+
/// <summary>
448+
/// Returns the interface for a QualifiedSelection contained by a statement similar to "Implements IClass1"
449+
/// </summary>
450+
/// <param name="declarations"></param>
451+
/// <param name="selection"></param>
452+
/// <returns></returns>
453+
[SuppressMessage("ReSharper", "LoopCanBeConvertedToQuery")]
454+
public static Declaration FindInterface(this IEnumerable<Declaration> declarations, QualifiedSelection selection)
455+
{
456+
foreach (var declaration in declarations.FindInterfaces())
457+
{
458+
foreach (var reference in declaration.References)
459+
{
460+
var implementsStmt = reference.Context.Parent as VBAParser.ImplementsStmtContext;
461+
462+
if (implementsStmt == null) { continue; }
463+
464+
if (reference.QualifiedModuleName == selection.QualifiedName &&
465+
(implementsStmt.GetSelection().Contains(selection.Selection)
466+
|| reference.Selection.Contains(selection.Selection)))
467+
{
468+
return declaration;
469+
}
470+
}
471+
}
472+
473+
return null;
474+
}
444475
}
445476
}
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Rubberduck.Common;
5+
using Rubberduck.Parsing.Grammar;
6+
using Rubberduck.Parsing.Symbols;
7+
using Rubberduck.Parsing.VBA;
8+
using Rubberduck.UI;
9+
using Rubberduck.VBEditor;
10+
11+
namespace Rubberduck.Refactorings.ImplementInterface
12+
{
13+
public class ImplementInterfaceRefactoring : IRefactoring
14+
{
15+
private readonly List<Declaration> _declarations;
16+
private readonly IActiveCodePaneEditor _editor;
17+
private Declaration _targetInterface;
18+
private Declaration _targetClass;
19+
private readonly IMessageBox _messageBox;
20+
21+
const string MemberBody = " Err.Raise 5";
22+
23+
public ImplementInterfaceRefactoring(RubberduckParserState state, IActiveCodePaneEditor editor, IMessageBox messageBox)
24+
{
25+
_declarations = state.AllDeclarations.ToList();
26+
_editor = editor;
27+
_messageBox = messageBox;
28+
}
29+
30+
public void Refactor()
31+
{
32+
var selection = _editor.GetSelection();
33+
34+
if (!selection.HasValue)
35+
{
36+
_messageBox.Show("Invalid selection.", "Rubberduck - Implement Interface",
37+
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation);
38+
return;
39+
}
40+
41+
Refactor(selection.Value);
42+
}
43+
44+
public void Refactor(QualifiedSelection selection)
45+
{
46+
_targetInterface = _declarations.FindInterface(selection);
47+
48+
_targetClass =
49+
_declarations.SingleOrDefault(
50+
d =>
51+
!d.IsBuiltIn && d.DeclarationType == DeclarationType.Class &&
52+
d.QualifiedSelection.QualifiedName == selection.QualifiedName);
53+
54+
if (_targetClass == null || _targetInterface == null)
55+
{
56+
return;
57+
}
58+
59+
ImplementMissingMembers();
60+
}
61+
62+
public void Refactor(Declaration target)
63+
{
64+
throw new NotImplementedException();
65+
}
66+
67+
private void ImplementMissingMembers()
68+
{
69+
var interfaceMembers = GetInterfaceMembers();
70+
var implementedMembers = GetImplementedMembers();
71+
72+
var nonImplementedMembers =
73+
interfaceMembers.Where(
74+
d =>
75+
!implementedMembers.Select(s => s.IdentifierName)
76+
.Contains(_targetInterface.ComponentName + "_" + d.IdentifierName)).ToList();
77+
78+
AddItems(nonImplementedMembers);
79+
}
80+
81+
private void AddItems(List<Declaration> members)
82+
{
83+
var module = _targetClass.QualifiedSelection.QualifiedName.Component.CodeModule;
84+
85+
members.Reverse();
86+
87+
foreach (var member in members)
88+
{
89+
module.InsertLines(module.CountOfDeclarationLines + 1, GetInterfaceMember(member));
90+
}
91+
}
92+
93+
private string GetInterfaceMember(Declaration member)
94+
{
95+
switch (GetMemberType(member))
96+
{
97+
case "Sub":
98+
return SubStmt(member);
99+
100+
case "Function":
101+
return FunctionStmt(member);
102+
103+
case "Property Get":
104+
return PropertyGetStmt(member);
105+
106+
case "Property Let":
107+
return PropertyLetStmt(member);
108+
109+
case "Property Set":
110+
return PropertySetStmt(member);
111+
}
112+
113+
return string.Empty;
114+
}
115+
116+
private string SubStmt(Declaration member)
117+
{
118+
var memberParams = GetParameters(member);
119+
120+
var memberSignature = "Public Sub " + _targetInterface.IdentifierName + "_" + member.IdentifierName + "(" +
121+
string.Join(", ", memberParams) + ")";
122+
123+
var memberCloseStatement = "End Sub" + Environment.NewLine;
124+
125+
return string.Join(Environment.NewLine, memberSignature, MemberBody, memberCloseStatement);
126+
}
127+
128+
private string FunctionStmt(Declaration member)
129+
{
130+
var memberParams = GetParameters(member);
131+
132+
var memberSignature = "Public Function " + _targetInterface.IdentifierName + "_" + member.IdentifierName + "(" +
133+
string.Join(", ", memberParams) + ")" + " As " + member.AsTypeName;
134+
135+
var memberCloseStatement = "End Function" + Environment.NewLine;
136+
137+
return string.Join(Environment.NewLine, memberSignature, MemberBody, memberCloseStatement);
138+
}
139+
140+
private string PropertyGetStmt(Declaration member)
141+
{
142+
var memberParams = GetParameters(member);
143+
144+
var memberSignature = "Public Property Get " + _targetInterface.IdentifierName + "_" + member.IdentifierName + "(" +
145+
string.Join(", ", memberParams) + ")" + " As " + member.AsTypeName;
146+
147+
var memberCloseStatement = "End Property" + Environment.NewLine;
148+
149+
return string.Join(Environment.NewLine, memberSignature, MemberBody, memberCloseStatement);
150+
}
151+
152+
private string PropertyLetStmt(Declaration member)
153+
{
154+
var memberParams = GetParameters(member);
155+
156+
var memberSignature = "Public Property Let " + _targetInterface.IdentifierName + "_" + member.IdentifierName +
157+
"(" + string.Join(", ", memberParams) + ")";
158+
159+
var memberCloseStatement = "End Property" + Environment.NewLine;
160+
161+
return string.Join(Environment.NewLine, memberSignature, MemberBody, memberCloseStatement);
162+
}
163+
164+
private string PropertySetStmt(Declaration member)
165+
{
166+
var memberParams = GetParameters(member);
167+
168+
var memberSignature = "Public Property Set " + _targetInterface.IdentifierName + "_" + member.IdentifierName +
169+
"(" + string.Join(", ", memberParams) + ")";
170+
171+
var memberCloseStatement = "End Property" + Environment.NewLine;
172+
173+
return string.Join(Environment.NewLine, memberSignature, MemberBody, memberCloseStatement);
174+
}
175+
176+
private List<Parameter> GetParameters(Declaration member)
177+
{
178+
var parameters = _declarations.Where(item => item.DeclarationType == DeclarationType.Parameter &&
179+
item.ParentScope == member.Scope)
180+
.OrderBy(o => o.Selection.StartLine)
181+
.ThenBy(t => t.Selection.StartColumn)
182+
.Select(p => new Parameter
183+
{
184+
ParamAccessibility = ((VBAParser.ArgContext)p.Context).BYREF() == null ? Tokens.ByVal : Tokens.ByRef,
185+
ParamName = p.IdentifierName,
186+
ParamType = p.AsTypeName
187+
})
188+
.ToList();
189+
190+
if (member.DeclarationType == DeclarationType.PropertyGet)
191+
{
192+
parameters.Remove(parameters.Last());
193+
}
194+
195+
return parameters;
196+
}
197+
198+
private IEnumerable<Declaration> GetInterfaceMembers()
199+
{
200+
return _declarations.FindInterfaceMembers()
201+
.Where(d => d.ComponentName == _targetInterface.IdentifierName)
202+
.OrderBy(d => d.Selection.StartLine)
203+
.ThenBy(d => d.Selection.StartColumn);
204+
}
205+
206+
private IEnumerable<Declaration> GetImplementedMembers()
207+
{
208+
return _declarations.FindInterfaceImplementationMembers()
209+
.Where(item => item.Project.Equals(_targetInterface.Project)
210+
&& item.ComponentName == _targetClass.IdentifierName
211+
&& item.IdentifierName.StartsWith(_targetInterface.ComponentName + "_")
212+
&& !item.Equals(_targetClass))
213+
.OrderBy(d => d.Selection.StartLine)
214+
.ThenBy(d => d.Selection.StartColumn);
215+
}
216+
217+
private string GetMemberType(Declaration member)
218+
{
219+
var context = member.Context;
220+
221+
var subStmtContext = context as VBAParser.SubStmtContext;
222+
if (subStmtContext != null)
223+
{
224+
return Tokens.Sub;
225+
}
226+
227+
var functionStmtContext = context as VBAParser.FunctionStmtContext;
228+
if (functionStmtContext != null)
229+
{
230+
return Tokens.Function;
231+
}
232+
233+
var propertyGetStmtContext = context as VBAParser.PropertyGetStmtContext;
234+
if (propertyGetStmtContext != null)
235+
{
236+
return Tokens.Property + " " + Tokens.Get;
237+
}
238+
239+
var propertyLetStmtContext = context as VBAParser.PropertyLetStmtContext;
240+
if (propertyLetStmtContext != null)
241+
{
242+
return Tokens.Property + " " + Tokens.Let;
243+
}
244+
245+
var propertySetStmtContext = context as VBAParser.PropertySetStmtContext;
246+
if (propertySetStmtContext != null)
247+
{
248+
return Tokens.Property + " " + Tokens.Set;
249+
}
250+
251+
return string.Empty;
252+
}
253+
}
254+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace Rubberduck.Refactorings.ImplementInterface
2+
{
3+
public class Parameter
4+
{
5+
public string ParamAccessibility { get; set; }
6+
public string ParamName { get; set; }
7+
public string ParamType { get; set; }
8+
9+
public override string ToString()
10+
{
11+
return ParamAccessibility + " " + ParamName + " As " + ParamType;
12+
}
13+
}
14+
}

RetailCoder.VBE/Root/CommandBarsModule.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ private IMenuItem GetRefactoringsParentMenu()
191191
_kernel.Get<RefactorEncapsulateFieldCommandMenuItem>(),
192192
_kernel.Get<RefactorMoveCloserToUsageCommandMenuItem>(),
193193
_kernel.Get<RefactorExtractInterfaceCommandMenuItem>(),
194+
_kernel.Get<RefactorImplementInterfaceCommandMenuItem>()
194195
};
195196
return new RefactoringsParentMenu(items);
196197
}

RetailCoder.VBE/Rubberduck.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,8 @@
320320
<Compile Include="Refactorings\ExtractInterface\ExtractInterfacePresenterFactory.cs" />
321321
<Compile Include="Refactorings\ExtractInterface\IExtractInterfaceView.cs" />
322322
<Compile Include="Refactorings\ExtractInterface\InterfaceMember.cs" />
323+
<Compile Include="Refactorings\ImplementInterface\ImplementInterfaceRefactoring.cs" />
324+
<Compile Include="Refactorings\ImplementInterface\Parameter.cs" />
323325
<Compile Include="Refactorings\IntroduceField\IntroduceFieldRefactoring.cs" />
324326
<Compile Include="Refactorings\IntroduceParameter\IntroduceParameterRefactoring.cs" />
325327
<Compile Include="Refactorings\MoveCloserToUsage\MoveCloserToUsageRefactoring.cs" />
@@ -418,6 +420,7 @@
418420
<Compile Include="UI\Command\MenuItems\ParentMenus\SmartIndenterParentMenu.cs" />
419421
<Compile Include="UI\Command\MenuItems\ParserStateCommandBar.cs" />
420422
<Compile Include="UI\Command\MenuItems\RefactorEncapsulateFieldCommandMenuItem.cs" />
423+
<Compile Include="UI\Command\MenuItems\RefactorImplementInterfaceCommandMenuItem.cs" />
421424
<Compile Include="UI\Command\MenuItems\RefactorExtractInterfaceCommandMenuItem.cs" />
422425
<Compile Include="UI\Command\MenuItems\RefactorIntroduceFieldCommandMenuItem.cs" />
423426
<Compile Include="UI\Command\MenuItems\RefactorIntroduceParameterCommandMenuItem.cs" />
@@ -432,6 +435,7 @@
432435
<Compile Include="UI\Command\MenuItems\ParentMenus\RefactoringsParentMenu.cs" />
433436
<Compile Include="UI\Command\MenuItems\RefactorRemoveParametersCommandMenuItem.cs" />
434437
<Compile Include="UI\Command\Refactorings\RefactorEncapsulateFieldCommand.cs" />
438+
<Compile Include="UI\Command\Refactorings\RefactorImplementInterfaceCommand.cs" />
435439
<Compile Include="UI\Command\Refactorings\RefactorExtractInterfaceCommand.cs" />
436440
<Compile Include="UI\Command\Refactorings\RefactorIntroduceFieldCommand.cs" />
437441
<Compile Include="UI\Command\Refactorings\RefactorIntroduceParameterCommand.cs" />

RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/RefactoringsParentMenu.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public enum RefactoringsMenuItemDisplayOrder
2222
IntroduceField,
2323
EncapsulateField,
2424
MoveCloserToUsage,
25-
ExtractInterface
25+
ExtractInterface,
26+
ImplementInterface
2627
}
2728
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Windows.Input;
2+
using Rubberduck.Parsing.VBA;
3+
using Rubberduck.UI.Command.MenuItems.ParentMenus;
4+
5+
namespace Rubberduck.UI.Command.MenuItems
6+
{
7+
public class RefactorImplementInterfaceCommandMenuItem : CommandMenuItemBase
8+
{
9+
public RefactorImplementInterfaceCommandMenuItem(ICommand command)
10+
: base(command)
11+
{
12+
}
13+
14+
public override string Key { get { return "RefactorMenu_ImplementInterface"; } }
15+
public override int DisplayOrder { get { return (int)RefactoringsMenuItemDisplayOrder.ImplementInterface; } }
16+
17+
public override bool EvaluateCanExecute(RubberduckParserState state)
18+
{
19+
return state.Status == ParserState.Ready;
20+
}
21+
}
22+
}

0 commit comments

Comments
 (0)