Skip to content

Commit 555b90c

Browse files
committed
Added Extract Interface Command Menu Item to Code Explorer window.
1 parent 3ebc6e4 commit 555b90c

File tree

7 files changed

+116
-32
lines changed

7 files changed

+116
-32
lines changed

Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerViewModel.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ private void ExecuteRemoveCommand(object param)
393393
public AddRemoveReferencesCommand AddRemoveReferencesCommand { get; set; }
394394
public ICommand ClearSearchCommand { get; }
395395
public CommandBase SyncCodePaneCommand { get; }
396+
public CodeExplorerExtractInterfaceCommand CodeExplorerExtractInterfaceCommand { get; set; }
396397

397398
public ICodeExplorerNode FindVisibleNodeForDeclaration(Declaration declaration)
398399
{

Rubberduck.Core/UI/CodeExplorer/CodeExplorerControl.xaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
<BitmapImage x:Key="AddRemoveReferencesImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/node-select-all.png" />
4444
<BitmapImage x:Key="SyncImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Custom/PNG/SyncArrows.png" />
4545
<BitmapImage x:Key="FontSizeImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/edit-size.png" />
46+
<BitmapImage x:Key="ExtractInterfaceImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Custom/PNG/ExtractInterface.png" />
4647

4748
<converters:BooleanToNullableDoubleConverter x:Key="BoolToDouble" />
4849
<converters:BoolToHiddenVisibilityConverter x:Key="BoolToHiddenVisibility" />
@@ -438,6 +439,13 @@
438439
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=Rename}"
439440
Command="{Binding RenameCommand}"
440441
CommandParameter="{Binding SelectedItem, Mode=OneWay}" />
442+
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.CodeExplorer.CodeExplorerUI, Key=CodeExplorer_ExtractInterfaceText}"
443+
Command="{Binding CodeExplorerExtractInterfaceCommand}"
444+
CommandParameter="{Binding SelectedItem, Mode=OneWay}">
445+
<MenuItem.Icon>
446+
<Image Source="{StaticResource ExtractInterfaceImage}" />
447+
</MenuItem.Icon>
448+
</MenuItem>
441449
<Separator />
442450
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=References_Caption}"
443451
Command="{Binding AddRemoveReferencesCommand}"
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Rubberduck.Navigation.CodeExplorer;
2+
using Rubberduck.Parsing.VBA;
3+
using Rubberduck.Refactorings.ExtractInterface;
4+
using Rubberduck.UI.Command.Refactorings.Notifiers;
5+
using Rubberduck.VBEditor.Events;
6+
using System;
7+
using System.Collections.Generic;
8+
9+
namespace Rubberduck.UI.CodeExplorer.Commands
10+
{
11+
public class CodeExplorerExtractInterfaceCommand : CodeExplorerCommandBase
12+
{
13+
private static readonly Type[] ApplicableNodes =
14+
{
15+
typeof(CodeExplorerComponentViewModel)
16+
};
17+
18+
private readonly IParserStatusProvider _state;
19+
private readonly ExtractInterfaceRefactoring _refactoring;
20+
private readonly ExtractInterfaceFailedNotifier _failureNotifier;
21+
22+
public CodeExplorerExtractInterfaceCommand(
23+
ExtractInterfaceRefactoring refactoring,
24+
IParserStatusProvider state,
25+
ExtractInterfaceFailedNotifier failureNotifier,
26+
IVbeEvents vbeEvents)
27+
: base(vbeEvents)
28+
{
29+
_state = state;
30+
_refactoring = refactoring;
31+
_failureNotifier = failureNotifier;
32+
AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
33+
}
34+
35+
public sealed override IEnumerable<Type> ApplicableNodeTypes => ApplicableNodes;
36+
37+
private bool SpecialEvaluateCanExecute(object parameter)
38+
{
39+
return _state.Status == ParserState.Ready &&
40+
parameter is CodeExplorerComponentViewModel node &&
41+
//node.Declaration.DeclarationType.HasFlag(DeclarationType.ClassModule) &&
42+
//node.Children.Any(child => child.Declaration.DeclarationType.HasFlag(DeclarationType.Member));
43+
ExtractInterfaceRefactoring.CanExecute((RubberduckParserState)_state, node.QualifiedSelection.Value.QualifiedName);
44+
}
45+
46+
protected override void OnExecute(object parameter)
47+
{
48+
if (_state.Status != ParserState.Ready ||
49+
!(parameter is CodeExplorerItemViewModel node) ||
50+
node.Declaration == null)
51+
{
52+
return;
53+
}
54+
55+
_refactoring.Refactor(node.Declaration);
56+
}
57+
}
58+
}

Rubberduck.Core/UI/Command/Refactorings/RefactorExtractInterfaceCommand.cs

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace Rubberduck.UI.Command.Refactorings
1616
public class RefactorExtractInterfaceCommand : RefactorCodePaneCommandBase
1717
{
1818
private readonly RubberduckParserState _state;
19-
19+
2020
public RefactorExtractInterfaceCommand(ExtractInterfaceRefactoring refactoring, ExtractInterfaceFailedNotifier extractInterfaceFailedNotifier, RubberduckParserState state, ISelectionService selectionService)
2121
:base(refactoring, extractInterfaceFailedNotifier, selectionService, state)
2222
{
@@ -32,41 +32,14 @@ private bool SpecializedEvaluateCanExecute(object parameter)
3232
{
3333
return false;
3434
}
35-
36-
var interfaceClass = _state.AllUserDeclarations.SingleOrDefault(item =>
37-
item.QualifiedName.QualifiedModuleName.Equals(activeSelection.Value.QualifiedName)
38-
&& ModuleTypes.Contains(item.DeclarationType));
39-
40-
if (interfaceClass == null)
41-
{
42-
return false;
43-
}
44-
45-
// interface class must have members to be implementable
46-
var hasMembers = _state.AllUserDeclarations.Any(item =>
47-
item.DeclarationType.HasFlag(DeclarationType.Member)
48-
&& item.ParentDeclaration != null
49-
&& item.ParentDeclaration.Equals(interfaceClass));
50-
51-
if (!hasMembers)
52-
{
53-
return false;
54-
}
55-
56-
var parseTree = _state.GetParseTree(interfaceClass.QualifiedName.QualifiedModuleName);
57-
var context = ((ParserRuleContext)parseTree).GetDescendents<VBAParser.ImplementsStmtContext>();
58-
59-
// true if active code pane is for a class/document/form module
60-
return !context.Any()
61-
&& !_state.IsNewOrModified(interfaceClass.QualifiedModuleName)
62-
&& !_state.IsNewOrModified(activeSelection.Value.QualifiedName);
35+
return ExtractInterfaceRefactoring.CanExecute(_state, activeSelection.Value.QualifiedName);
6336
}
6437

65-
private static readonly IReadOnlyList<DeclarationType> ModuleTypes = new[]
38+
private static readonly IReadOnlyList<DeclarationType> ModuleTypes = new[]
6639
{
6740
DeclarationType.ClassModule,
68-
DeclarationType.UserForm,
69-
DeclarationType.Document,
41+
DeclarationType.UserForm,
42+
DeclarationType.Document,
7043
};
7144
}
7245
}

Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Linq;
33
using NLog;
4+
using Rubberduck.Parsing;
45
using Rubberduck.Parsing.Grammar;
56
using Rubberduck.Parsing.Rewriter;
67
using Rubberduck.Parsing.Symbols;
@@ -135,5 +136,36 @@ private string GetInterfaceModuleBody(ExtractInterfaceModel model)
135136
DeclarationType.Document,
136137
DeclarationType.UserForm
137138
};
139+
140+
public static bool CanExecute(RubberduckParserState state, QualifiedModuleName qualifiedName)
141+
{
142+
var interfaceClass = state.AllUserDeclarations.SingleOrDefault(item =>
143+
item.QualifiedName.QualifiedModuleName.Equals(qualifiedName)
144+
&& ModuleTypes.Contains(item.DeclarationType));
145+
146+
if (interfaceClass == null)
147+
{
148+
return false;
149+
}
150+
151+
// interface class must have members to be implementable
152+
var hasMembers = state.AllUserDeclarations.Any(item =>
153+
item.DeclarationType.HasFlag(DeclarationType.Member)
154+
&& item.ParentDeclaration != null
155+
&& item.ParentDeclaration.Equals(interfaceClass));
156+
157+
if (!hasMembers)
158+
{
159+
return false;
160+
}
161+
162+
var parseTree = state.GetParseTree(interfaceClass.QualifiedName.QualifiedModuleName);
163+
var context = ((Antlr4.Runtime.ParserRuleContext)parseTree).GetDescendents<VBAParser.ImplementsStmtContext>();
164+
165+
// true if active code pane is for a class/document/form module
166+
return !context.Any()
167+
&& !state.IsNewOrModified(interfaceClass.QualifiedModuleName)
168+
&& !state.IsNewOrModified(qualifiedName);
169+
}
138170
}
139171
}

Rubberduck.Resources/CodeExplorer/CodeExplorerUI.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Rubberduck.Resources/CodeExplorer/CodeExplorerUI.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,4 +440,7 @@ Continue?</value>
440440

441441
{0}</value>
442442
</data>
443+
<data name="CodeExplorer_ExtractInterfaceText" xml:space="preserve">
444+
<value>Extract Interface</value>
445+
</data>
443446
</root>

0 commit comments

Comments
 (0)