Skip to content

Commit

Permalink
Merge pull request #5497 from MDoerner/AnnotateCommand
Browse files Browse the repository at this point in the history
Annotate Declaration Command
  • Loading branch information
retailcoder committed Jun 7, 2020
2 parents 14acc3f + 5f73f57 commit 7accdd2
Show file tree
Hide file tree
Showing 104 changed files with 4,606 additions and 283 deletions.
Expand Up @@ -4,8 +4,9 @@

namespace Rubberduck.CodeAnalysis.Inspections.Extensions
{
internal static class DeclarationTypeExtensions
public static class DeclarationTypeExtensions
{
//ToDo: Move this to resources. (This will require moving resource lookups to Core.)
public static string ToLocalizedString(this DeclarationType type)
{
return RubberduckUI.ResourceManager.GetString("DeclarationType_" + type, CultureInfo.CurrentUICulture);
Expand Down
214 changes: 107 additions & 107 deletions Rubberduck.CodeAnalysis/Properties/CodeInspectionDefaults.Designer.cs

Large diffs are not rendered by default.

Expand Up @@ -16,7 +16,7 @@
<CodeInspection Name="IllegalAnnotationInspection" Severity="Error" InspectionType="RubberduckOpportunities" />
<CodeInspection Name="RedundantByRefModifierInspection" Severity="DoNotShow" InspectionType="CodeQualityIssues" />
<CodeInspection Name="MissingAttributeInspection" Severity="Warning" InspectionType="RubberduckOpportunities" />
<CodeInspection Name="AttributeOutOfSyncInspection" Severity="Warning" InspectionType="RubberduckOpportunities" />
<CodeInspection Name="AttributeValueOutOfSyncInspection" Severity="Warning" InspectionType="RubberduckOpportunities" />
<CodeInspection Name="MissingAnnotationArgumentInspection" Severity="Error" InspectionType="CodeQualityIssues" />
<CodeInspection Name="MissingMemberAnnotationInspection" Severity="Error" InspectionType="RubberduckOpportunities" />
<CodeInspection Name="ModuleScopeDimKeywordInspection" Severity="Suggestion" InspectionType="LanguageOpportunities" />
Expand Down
14 changes: 13 additions & 1 deletion Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerViewModel.cs
Expand Up @@ -14,6 +14,7 @@
using Rubberduck.VBEditor.SafeComWrappers;
using System.Windows;
using System.Windows.Input;
using Rubberduck.Parsing.Annotations;
using Rubberduck.Parsing.UIContext;
using Rubberduck.Templates;
using Rubberduck.UI.CodeExplorer.Commands.DragAndDrop;
Expand Down Expand Up @@ -55,7 +56,8 @@ public sealed class CodeExplorerViewModel : ViewModelBase
IUiDispatcher uiDispatcher,
IVBE vbe,
ITemplateProvider templateProvider,
ICodeExplorerSyncProvider syncProvider)
ICodeExplorerSyncProvider syncProvider,
IEnumerable<IAnnotation> annotations)
{
_state = state;
_state.StateChanged += HandleStateChanged;
Expand All @@ -67,6 +69,7 @@ public sealed class CodeExplorerViewModel : ViewModelBase
_uiDispatcher = uiDispatcher;
_vbe = vbe;
_templateProvider = templateProvider;
Annotations = annotations.ToList();

CollapseAllSubnodesCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteCollapseNodes, EvaluateCanSwitchNodeState);
ExpandAllSubnodesCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteExpandNodes, EvaluateCanSwitchNodeState);
Expand All @@ -93,6 +96,8 @@ public sealed class CodeExplorerViewModel : ViewModelBase
new ObservableCollection<Template>(_templateProvider.GetTemplates().Where(t => t.IsUserDefined)
.OrderBy(t => t.Name));

public IEnumerable<IAnnotation> Annotations { get; }

private ICodeExplorerNode _selectedItem;
public ICodeExplorerNode SelectedItem
{
Expand All @@ -111,6 +116,8 @@ public ICodeExplorerNode SelectedItem

OnPropertyChanged(nameof(ExportVisibility));
OnPropertyChanged(nameof(ExportAllVisibility));
OnPropertyChanged(nameof(CanBeAnnotated));
OnPropertyChanged(nameof(AnyTemplatesCanExecute));
}
}

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

public bool CanBeAnnotated =>
AnnotateDeclarationCommand.CanExecuteForNode(SelectedItem)
&& Annotations.Any(annotation => AnnotateDeclarationCommand.CanExecute((annotation, SelectedItem)));

private CodeExplorerSortOrder _sortOrder = CodeExplorerSortOrder.Name;
public CodeExplorerSortOrder SortOrder
{
Expand Down Expand Up @@ -376,6 +387,7 @@ private void ExecuteRemoveCommand(object param)
public AddTestComponentCommand AddTestModuleCommand { get; set; }
public AddTestModuleWithStubsCommand AddTestModuleWithStubsCommand { get; set; }
public AddTemplateCommand AddTemplateCommand { get; set; }
public AnnotateDeclarationCommand AnnotateDeclarationCommand { get; set; }
public OpenDesignerCommand OpenDesignerCommand { get; set; }
public OpenProjectPropertiesCommand OpenProjectPropertiesCommand { get; set; }
public SetAsStartupProjectCommand SetAsStartupProjectCommand { get; set; }
Expand Down
3 changes: 3 additions & 0 deletions Rubberduck.Core/Rubberduck.Core.csproj
Expand Up @@ -117,6 +117,9 @@
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="UI\Refactorings\AnnotateDeclaration\AnnotateDeclarationView.xaml.cs">
<DependentUpon>AnnotateDeclarationView.xaml</DependentUpon>
</Compile>
<Compile Update="UI\Refactorings\MoveFolder\MoveMultipleFoldersView.xaml.cs">
<DependentUpon>MoveMultipleFoldersView.xaml</DependentUpon>
</Compile>
Expand Down
33 changes: 33 additions & 0 deletions Rubberduck.Core/UI/CodeExplorer/CodeExplorerControl.xaml
Expand Up @@ -21,6 +21,7 @@
</ResourceDictionary.MergedDictionaries>

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

<BitmapImage x:Key="RefreshImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/arrow-circle-double.png" />
<BitmapImage x:Key="CollapseNodesImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/folder.png" />
Expand Down Expand Up @@ -51,6 +52,9 @@
<converters:InvertBoolValueConverter x:Key="NotBool" />
<converters:CodeExplorerNodeToIconConverter x:Key="NodeToIcon" />
<converters:AccessibilityToIconConverter x:Key="AccessibilityToIcon" />
<converters:AnnotationToCodeStringConverter x:Key="AnnotationToCodeString" />
<converters:AnnotateDeclarationCommandParameterToTupleConverter x:Key="AnnotateDeclarationCommandParameterToTuple" />
<converters:AnnotateDeclarationCommandCEVisibilityConverter x:Key="AnnotateDeclarationCommandVisibility" />

<CompositeCollection x:Key="AddModuleCommands" x:Shared="False">
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.CodeExplorer.CodeExplorerUI, Key=CodeExplorer_AddExistingFileText}"
Expand Down Expand Up @@ -430,6 +434,35 @@
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.CodeExplorer.CodeExplorerUI, Key=CodeExplorer_MoveToFolder}"
Command="{Binding MoveToFolderCommand}"
CommandParameter="{Binding SelectedItem, Mode=OneWay}" />
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.CodeExplorer.CodeExplorerUI, Key=CodeExplorer_Annotate}" IsEnabled="{Binding CanBeAnnotated}">
<MenuItem.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource AnnotationsViewSource}}" />
</CompositeCollection>
</MenuItem.ItemsSource>
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.AnnotateDeclarationCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
<Setter Property="Header" Value="{Binding Path=., Converter={StaticResource AnnotationToCodeString}}" />
<Setter Property="CommandParameter">
<Setter.Value>
<MultiBinding Converter="{StaticResource AnnotateDeclarationCommandParameterToTuple}">
<Binding Path="." />
<Binding Path="DataContext.SelectedItem" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter ="{StaticResource AnnotateDeclarationCommandVisibility}">
<Binding Path="." />
<Binding Path="DataContext.SelectedItem" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.CodeExplorer.CodeExplorerUI, Key=CodeExplorer_ExtractInterfaceText}"
Command="{Binding CodeExplorerExtractInterfaceCommand}"
CommandParameter="{Binding SelectedItem, Mode=OneWay}">
Expand Down
161 changes: 161 additions & 0 deletions Rubberduck.Core/UI/CodeExplorer/Commands/AnnotateDeclarationCommand.cs
@@ -0,0 +1,161 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Rubberduck.Navigation.CodeExplorer;
using Rubberduck.Parsing.Annotations;
using Rubberduck.Parsing.Symbols;
using Rubberduck.Parsing.VBA;
using Rubberduck.Refactorings;
using Rubberduck.Refactorings.AnnotateDeclaration;
using Rubberduck.Refactorings.Exceptions;
using Rubberduck.UI.Command.Refactorings.Notifiers;
using Rubberduck.VBEditor.Events;

namespace Rubberduck.UI.CodeExplorer.Commands
{
public class AnnotateDeclarationCommand : CodeExplorerCommandBase
{
private static readonly Type[] ApplicableNodes =
{
typeof(CodeExplorerComponentViewModel),
typeof(CodeExplorerMemberViewModel)
};

private readonly RubberduckParserState _state;

private readonly IRefactoringAction<AnnotateDeclarationModel> _annotateAction;
private readonly IRefactoringFailureNotifier _failureNotifier;
private readonly IRefactoringUserInteraction<AnnotateDeclarationModel> _userInteraction;

public AnnotateDeclarationCommand(
AnnotateDeclarationRefactoringAction annotateAction,
AnnotateDeclarationFailedNotifier failureNotifier,
RefactoringUserInteraction<IAnnotateDeclarationPresenter, AnnotateDeclarationModel> userInteraction,
IVbeEvents vbeEvents,
RubberduckParserState state)
: base(vbeEvents)
{
_annotateAction = annotateAction;
_failureNotifier = failureNotifier;
_userInteraction = userInteraction;
_state = state;

AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
}

public override IEnumerable<Type> ApplicableNodeTypes => new[] { typeof(System.ValueTuple<IAnnotation, ICodeExplorerNode>) };

private bool SpecialEvaluateCanExecute(object parameter)
{
if (parameter is System.ValueTuple<IAnnotation, ICodeExplorerNode> data)
{
var (annotation, node) = data;
return EvaluateCanExecute(annotation, node);
}

return false;
}

private bool EvaluateCanExecute(IAnnotation annotation, ICodeExplorerNode node)
{
var target = node?.Declaration;

if (target == null
|| annotation == null
|| !CanExecuteForNode(node))
{
return false;
}

if (!annotation.AllowMultiple
&& target.Annotations.Any(pta => pta.Annotation.Equals(annotation)))
{
return false;
}

var targetType = target.DeclarationType;

switch (annotation.Target)
{
case AnnotationTarget.Member:
return targetType.HasFlag(DeclarationType.Member)
&& targetType != DeclarationType.LibraryFunction
&& targetType != DeclarationType.LibraryProcedure;
case AnnotationTarget.Module:
return targetType.HasFlag(DeclarationType.Module);
case AnnotationTarget.Variable:
return targetType.HasFlag(DeclarationType.Variable)
|| targetType.HasFlag(DeclarationType.Constant);
case AnnotationTarget.General:
return true;
case AnnotationTarget.Identifier:
return false;
default:
return false;
}
}

public bool CanExecuteForNode(ICodeExplorerNode node)
{
if (!ApplicableNodes.Contains(node.GetType())
|| !(node is CodeExplorerItemViewModel)
|| node.Declaration == null)
{
return false;
}

var target = node.Declaration;
var targetType = target.DeclarationType;

if (!targetType.HasFlag(DeclarationType.Module)
&& !targetType.HasFlag(DeclarationType.Variable)
&& !targetType.HasFlag(DeclarationType.Constant)
&& !targetType.HasFlag(DeclarationType.Member)
|| targetType == DeclarationType.LibraryFunction
|| targetType == DeclarationType.LibraryProcedure)
{
return false;
}

return !_state.IsNewOrModified(target.QualifiedModuleName);
}

protected override void OnExecute(object parameter)
{
if (!(parameter is System.ValueTuple<IAnnotation, ICodeExplorerNode> data))
{
return;
}

var (annotation, node) = data;
var target = node?.Declaration;
try
{
var model = ModelFromParameter(annotation, target);
if (!annotation.AllowedArguments.HasValue
|| annotation.AllowedArguments.Value > 0)
{
model = _userInteraction.UserModifiedModel(model);
}

_annotateAction.Refactor(model);
}
catch (RefactoringAbortedException)
{}
catch (RefactoringException exception)
{
_failureNotifier.Notify(exception);
}
}

private AnnotateDeclarationModel ModelFromParameter(IAnnotation annotation, Declaration target)
{
if (target == null)
{
throw new TargetDeclarationIsNullException();
}

return new AnnotateDeclarationModel(target, annotation);
}
}
}

0 comments on commit 7accdd2

Please sign in to comment.