Skip to content

Commit 7accdd2

Browse files
authored
Merge pull request #5497 from MDoerner/AnnotateCommand
Annotate Declaration Command
2 parents 14acc3f + 5f73f57 commit 7accdd2

File tree

104 files changed

+4606
-283
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+4606
-283
lines changed

Rubberduck.CodeAnalysis/Inspections/Extensions/DeclarationTypeExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
namespace Rubberduck.CodeAnalysis.Inspections.Extensions
66
{
7-
internal static class DeclarationTypeExtensions
7+
public static class DeclarationTypeExtensions
88
{
9+
//ToDo: Move this to resources. (This will require moving resource lookups to Core.)
910
public static string ToLocalizedString(this DeclarationType type)
1011
{
1112
return RubberduckUI.ResourceManager.GetString("DeclarationType_" + type, CultureInfo.CurrentUICulture);

Rubberduck.CodeAnalysis/Properties/CodeInspectionDefaults.Designer.cs

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

Rubberduck.CodeAnalysis/Properties/CodeInspectionDefaults.settings

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<CodeInspection Name="IllegalAnnotationInspection" Severity="Error" InspectionType="RubberduckOpportunities" />
1717
<CodeInspection Name="RedundantByRefModifierInspection" Severity="DoNotShow" InspectionType="CodeQualityIssues" />
1818
<CodeInspection Name="MissingAttributeInspection" Severity="Warning" InspectionType="RubberduckOpportunities" />
19-
<CodeInspection Name="AttributeOutOfSyncInspection" Severity="Warning" InspectionType="RubberduckOpportunities" />
19+
<CodeInspection Name="AttributeValueOutOfSyncInspection" Severity="Warning" InspectionType="RubberduckOpportunities" />
2020
<CodeInspection Name="MissingAnnotationArgumentInspection" Severity="Error" InspectionType="CodeQualityIssues" />
2121
<CodeInspection Name="MissingMemberAnnotationInspection" Severity="Error" InspectionType="RubberduckOpportunities" />
2222
<CodeInspection Name="ModuleScopeDimKeywordInspection" Severity="Suggestion" InspectionType="LanguageOpportunities" />

Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerViewModel.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using Rubberduck.VBEditor.SafeComWrappers;
1515
using System.Windows;
1616
using System.Windows.Input;
17+
using Rubberduck.Parsing.Annotations;
1718
using Rubberduck.Parsing.UIContext;
1819
using Rubberduck.Templates;
1920
using Rubberduck.UI.CodeExplorer.Commands.DragAndDrop;
@@ -55,7 +56,8 @@ public CodeExplorerViewModel(
5556
IUiDispatcher uiDispatcher,
5657
IVBE vbe,
5758
ITemplateProvider templateProvider,
58-
ICodeExplorerSyncProvider syncProvider)
59+
ICodeExplorerSyncProvider syncProvider,
60+
IEnumerable<IAnnotation> annotations)
5961
{
6062
_state = state;
6163
_state.StateChanged += HandleStateChanged;
@@ -67,6 +69,7 @@ public CodeExplorerViewModel(
6769
_uiDispatcher = uiDispatcher;
6870
_vbe = vbe;
6971
_templateProvider = templateProvider;
72+
Annotations = annotations.ToList();
7073

7174
CollapseAllSubnodesCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteCollapseNodes, EvaluateCanSwitchNodeState);
7275
ExpandAllSubnodesCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteExpandNodes, EvaluateCanSwitchNodeState);
@@ -93,6 +96,8 @@ public CodeExplorerViewModel(
9396
new ObservableCollection<Template>(_templateProvider.GetTemplates().Where(t => t.IsUserDefined)
9497
.OrderBy(t => t.Name));
9598

99+
public IEnumerable<IAnnotation> Annotations { get; }
100+
96101
private ICodeExplorerNode _selectedItem;
97102
public ICodeExplorerNode SelectedItem
98103
{
@@ -111,6 +116,8 @@ public ICodeExplorerNode SelectedItem
111116

112117
OnPropertyChanged(nameof(ExportVisibility));
113118
OnPropertyChanged(nameof(ExportAllVisibility));
119+
OnPropertyChanged(nameof(CanBeAnnotated));
120+
OnPropertyChanged(nameof(AnyTemplatesCanExecute));
114121
}
115122
}
116123

@@ -119,6 +126,10 @@ public ICodeExplorerNode SelectedItem
119126
&& BuiltInTemplates.Concat(UserDefinedTemplates)
120127
.Any(template => AddTemplateCommand.CanExecute((template.Name, SelectedItem)));
121128

129+
public bool CanBeAnnotated =>
130+
AnnotateDeclarationCommand.CanExecuteForNode(SelectedItem)
131+
&& Annotations.Any(annotation => AnnotateDeclarationCommand.CanExecute((annotation, SelectedItem)));
132+
122133
private CodeExplorerSortOrder _sortOrder = CodeExplorerSortOrder.Name;
123134
public CodeExplorerSortOrder SortOrder
124135
{
@@ -376,6 +387,7 @@ private void ExecuteRemoveCommand(object param)
376387
public AddTestComponentCommand AddTestModuleCommand { get; set; }
377388
public AddTestModuleWithStubsCommand AddTestModuleWithStubsCommand { get; set; }
378389
public AddTemplateCommand AddTemplateCommand { get; set; }
390+
public AnnotateDeclarationCommand AnnotateDeclarationCommand { get; set; }
379391
public OpenDesignerCommand OpenDesignerCommand { get; set; }
380392
public OpenProjectPropertiesCommand OpenProjectPropertiesCommand { get; set; }
381393
public SetAsStartupProjectCommand SetAsStartupProjectCommand { get; set; }

Rubberduck.Core/Rubberduck.Core.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@
117117
<DesignTime>True</DesignTime>
118118
<AutoGen>True</AutoGen>
119119
</Compile>
120+
<Compile Update="UI\Refactorings\AnnotateDeclaration\AnnotateDeclarationView.xaml.cs">
121+
<DependentUpon>AnnotateDeclarationView.xaml</DependentUpon>
122+
</Compile>
120123
<Compile Update="UI\Refactorings\MoveFolder\MoveMultipleFoldersView.xaml.cs">
121124
<DependentUpon>MoveMultipleFoldersView.xaml</DependentUpon>
122125
</Compile>

Rubberduck.Core/UI/CodeExplorer/CodeExplorerControl.xaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
</ResourceDictionary.MergedDictionaries>
2222

2323
<CollectionViewSource x:Key="BuiltInTemplatesViewSource" x:Name="BuiltInTemplatesView" Source="{Binding BuiltInTemplates}" />
24+
<CollectionViewSource x:Key="AnnotationsViewSource" x:Name="AnnotationsView" Source="{Binding Annotations}" />
2425

2526
<BitmapImage x:Key="RefreshImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/arrow-circle-double.png" />
2627
<BitmapImage x:Key="CollapseNodesImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/folder.png" />
@@ -51,6 +52,9 @@
5152
<converters:InvertBoolValueConverter x:Key="NotBool" />
5253
<converters:CodeExplorerNodeToIconConverter x:Key="NodeToIcon" />
5354
<converters:AccessibilityToIconConverter x:Key="AccessibilityToIcon" />
55+
<converters:AnnotationToCodeStringConverter x:Key="AnnotationToCodeString" />
56+
<converters:AnnotateDeclarationCommandParameterToTupleConverter x:Key="AnnotateDeclarationCommandParameterToTuple" />
57+
<converters:AnnotateDeclarationCommandCEVisibilityConverter x:Key="AnnotateDeclarationCommandVisibility" />
5458

5559
<CompositeCollection x:Key="AddModuleCommands" x:Shared="False">
5660
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.CodeExplorer.CodeExplorerUI, Key=CodeExplorer_AddExistingFileText}"
@@ -430,6 +434,35 @@
430434
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.CodeExplorer.CodeExplorerUI, Key=CodeExplorer_MoveToFolder}"
431435
Command="{Binding MoveToFolderCommand}"
432436
CommandParameter="{Binding SelectedItem, Mode=OneWay}" />
437+
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.CodeExplorer.CodeExplorerUI, Key=CodeExplorer_Annotate}" IsEnabled="{Binding CanBeAnnotated}">
438+
<MenuItem.ItemsSource>
439+
<CompositeCollection>
440+
<CollectionContainer Collection="{Binding Source={StaticResource AnnotationsViewSource}}" />
441+
</CompositeCollection>
442+
</MenuItem.ItemsSource>
443+
<MenuItem.ItemContainerStyle>
444+
<Style TargetType="MenuItem">
445+
<Setter Property="Command" Value="{Binding DataContext.AnnotateDeclarationCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
446+
<Setter Property="Header" Value="{Binding Path=., Converter={StaticResource AnnotationToCodeString}}" />
447+
<Setter Property="CommandParameter">
448+
<Setter.Value>
449+
<MultiBinding Converter="{StaticResource AnnotateDeclarationCommandParameterToTuple}">
450+
<Binding Path="." />
451+
<Binding Path="DataContext.SelectedItem" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}" />
452+
</MultiBinding>
453+
</Setter.Value>
454+
</Setter>
455+
<Setter Property="Visibility">
456+
<Setter.Value>
457+
<MultiBinding Converter ="{StaticResource AnnotateDeclarationCommandVisibility}">
458+
<Binding Path="." />
459+
<Binding Path="DataContext.SelectedItem" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}" />
460+
</MultiBinding>
461+
</Setter.Value>
462+
</Setter>
463+
</Style>
464+
</MenuItem.ItemContainerStyle>
465+
</MenuItem>
433466
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.CodeExplorer.CodeExplorerUI, Key=CodeExplorer_ExtractInterfaceText}"
434467
Command="{Binding CodeExplorerExtractInterfaceCommand}"
435468
CommandParameter="{Binding SelectedItem, Mode=OneWay}">
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Rubberduck.Navigation.CodeExplorer;
5+
using Rubberduck.Parsing.Annotations;
6+
using Rubberduck.Parsing.Symbols;
7+
using Rubberduck.Parsing.VBA;
8+
using Rubberduck.Refactorings;
9+
using Rubberduck.Refactorings.AnnotateDeclaration;
10+
using Rubberduck.Refactorings.Exceptions;
11+
using Rubberduck.UI.Command.Refactorings.Notifiers;
12+
using Rubberduck.VBEditor.Events;
13+
14+
namespace Rubberduck.UI.CodeExplorer.Commands
15+
{
16+
public class AnnotateDeclarationCommand : CodeExplorerCommandBase
17+
{
18+
private static readonly Type[] ApplicableNodes =
19+
{
20+
typeof(CodeExplorerComponentViewModel),
21+
typeof(CodeExplorerMemberViewModel)
22+
};
23+
24+
private readonly RubberduckParserState _state;
25+
26+
private readonly IRefactoringAction<AnnotateDeclarationModel> _annotateAction;
27+
private readonly IRefactoringFailureNotifier _failureNotifier;
28+
private readonly IRefactoringUserInteraction<AnnotateDeclarationModel> _userInteraction;
29+
30+
public AnnotateDeclarationCommand(
31+
AnnotateDeclarationRefactoringAction annotateAction,
32+
AnnotateDeclarationFailedNotifier failureNotifier,
33+
RefactoringUserInteraction<IAnnotateDeclarationPresenter, AnnotateDeclarationModel> userInteraction,
34+
IVbeEvents vbeEvents,
35+
RubberduckParserState state)
36+
: base(vbeEvents)
37+
{
38+
_annotateAction = annotateAction;
39+
_failureNotifier = failureNotifier;
40+
_userInteraction = userInteraction;
41+
_state = state;
42+
43+
AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
44+
}
45+
46+
public override IEnumerable<Type> ApplicableNodeTypes => new[] { typeof(System.ValueTuple<IAnnotation, ICodeExplorerNode>) };
47+
48+
private bool SpecialEvaluateCanExecute(object parameter)
49+
{
50+
if (parameter is System.ValueTuple<IAnnotation, ICodeExplorerNode> data)
51+
{
52+
var (annotation, node) = data;
53+
return EvaluateCanExecute(annotation, node);
54+
}
55+
56+
return false;
57+
}
58+
59+
private bool EvaluateCanExecute(IAnnotation annotation, ICodeExplorerNode node)
60+
{
61+
var target = node?.Declaration;
62+
63+
if (target == null
64+
|| annotation == null
65+
|| !CanExecuteForNode(node))
66+
{
67+
return false;
68+
}
69+
70+
if (!annotation.AllowMultiple
71+
&& target.Annotations.Any(pta => pta.Annotation.Equals(annotation)))
72+
{
73+
return false;
74+
}
75+
76+
var targetType = target.DeclarationType;
77+
78+
switch (annotation.Target)
79+
{
80+
case AnnotationTarget.Member:
81+
return targetType.HasFlag(DeclarationType.Member)
82+
&& targetType != DeclarationType.LibraryFunction
83+
&& targetType != DeclarationType.LibraryProcedure;
84+
case AnnotationTarget.Module:
85+
return targetType.HasFlag(DeclarationType.Module);
86+
case AnnotationTarget.Variable:
87+
return targetType.HasFlag(DeclarationType.Variable)
88+
|| targetType.HasFlag(DeclarationType.Constant);
89+
case AnnotationTarget.General:
90+
return true;
91+
case AnnotationTarget.Identifier:
92+
return false;
93+
default:
94+
return false;
95+
}
96+
}
97+
98+
public bool CanExecuteForNode(ICodeExplorerNode node)
99+
{
100+
if (!ApplicableNodes.Contains(node.GetType())
101+
|| !(node is CodeExplorerItemViewModel)
102+
|| node.Declaration == null)
103+
{
104+
return false;
105+
}
106+
107+
var target = node.Declaration;
108+
var targetType = target.DeclarationType;
109+
110+
if (!targetType.HasFlag(DeclarationType.Module)
111+
&& !targetType.HasFlag(DeclarationType.Variable)
112+
&& !targetType.HasFlag(DeclarationType.Constant)
113+
&& !targetType.HasFlag(DeclarationType.Member)
114+
|| targetType == DeclarationType.LibraryFunction
115+
|| targetType == DeclarationType.LibraryProcedure)
116+
{
117+
return false;
118+
}
119+
120+
return !_state.IsNewOrModified(target.QualifiedModuleName);
121+
}
122+
123+
protected override void OnExecute(object parameter)
124+
{
125+
if (!(parameter is System.ValueTuple<IAnnotation, ICodeExplorerNode> data))
126+
{
127+
return;
128+
}
129+
130+
var (annotation, node) = data;
131+
var target = node?.Declaration;
132+
try
133+
{
134+
var model = ModelFromParameter(annotation, target);
135+
if (!annotation.AllowedArguments.HasValue
136+
|| annotation.AllowedArguments.Value > 0)
137+
{
138+
model = _userInteraction.UserModifiedModel(model);
139+
}
140+
141+
_annotateAction.Refactor(model);
142+
}
143+
catch (RefactoringAbortedException)
144+
{}
145+
catch (RefactoringException exception)
146+
{
147+
_failureNotifier.Notify(exception);
148+
}
149+
}
150+
151+
private AnnotateDeclarationModel ModelFromParameter(IAnnotation annotation, Declaration target)
152+
{
153+
if (target == null)
154+
{
155+
throw new TargetDeclarationIsNullException();
156+
}
157+
158+
return new AnnotateDeclarationModel(target, annotation);
159+
}
160+
}
161+
}

0 commit comments

Comments
 (0)