From 534f7f752440c16b01a40bf8e1cfb8efd6c02f65 Mon Sep 17 00:00:00 2001 From: IvenBach Date: Wed, 15 Jan 2020 13:28:45 -0800 Subject: [PATCH 01/82] Simplify default expression --- .../UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs index 713a256f24..5047c33265 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs @@ -62,7 +62,7 @@ public virtual Func ToolTipText } public virtual bool BeginGroup => false; - public virtual int DisplayOrder => default(int); + public virtual int DisplayOrder => default; public void Localize() { From 52a04e9bedec256a888aeda6a5f388837ea485f2 Mon Sep 17 00:00:00 2001 From: IvenBach Date: Wed, 15 Jan 2020 13:31:36 -0800 Subject: [PATCH 02/82] Add instancing for Extract Interface Declaration's aren't correctly reflecting the exposed attribute. Need to work out *why*, when PublicNotCreatable is chosen in the IDE, it isn't reflected in Declaration.Attributes.ExposedAttributes.Values --- .../ExtractInterfaceView.xaml | 368 +++++++++--------- .../ExtractInterfaceViewModel.cs | 16 +- .../ExtractInterfaceRefactoring.cs | 6 + .../InteractiveRefactoringBase.cs | 3 +- Rubberduck.Resources/RubberduckUI.Designer.cs | 27 ++ Rubberduck.Resources/RubberduckUI.resx | 9 + .../Office/CommandBarButton.cs | 1 - 7 files changed, 248 insertions(+), 182 deletions(-) diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml index bb476c0667..75b9cb3ae7 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml @@ -1,179 +1,189 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs index a5960dd92f..734172d181 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs @@ -5,7 +5,6 @@ using NLog; using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.ExtractInterface; using Rubberduck.UI.Command; @@ -62,6 +61,21 @@ public bool IsValidInterfaceName } } + public bool IsInterfacePublicNotCreateable + { + get + { + try + { + return Convert.ToBoolean(Model.TargetDeclaration.Attributes.ExposedAttribute.Values.First()); + } + catch (FormatException) + { + return false; + } + } + } + private void ToggleSelection(bool value) { foreach (var item in Members) diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs index 6745a264e8..98c2ee5f4d 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs @@ -140,6 +140,12 @@ private void AddInterfaceClass(Declaration implementingClass, string interfaceNa private void AddInterfaceMembersToClass(ExtractInterfaceModel model, IModuleRewriter rewriter) { _implementInterfaceRefactoring.Refactor(model.SelectedMembers.Select(m => m.Member).ToList(), rewriter, model.InterfaceName); + + var classIsExposed = Convert.ToBoolean(model.TargetDeclaration.Attributes.ExposedAttribute.Values.First()); + if (classIsExposed) + { + model.TargetDeclaration.Attributes.AddExposedClassAttribute(); + } } private string GetInterfaceModuleBody(ExtractInterfaceModel model) diff --git a/Rubberduck.Refactorings/InteractiveRefactoringBase.cs b/Rubberduck.Refactorings/InteractiveRefactoringBase.cs index e8939954c8..d27dc94200 100644 --- a/Rubberduck.Refactorings/InteractiveRefactoringBase.cs +++ b/Rubberduck.Refactorings/InteractiveRefactoringBase.cs @@ -26,7 +26,8 @@ public abstract class InteractiveRefactoringBase : Refactori public override void Refactor(Declaration target) { - Refactor(InitializeModel(target)); + var model = InitializeModel(target); + Refactor(model); } protected void Refactor(TModel initialModel) diff --git a/Rubberduck.Resources/RubberduckUI.Designer.cs b/Rubberduck.Resources/RubberduckUI.Designer.cs index 171761f190..735c1b7314 100644 --- a/Rubberduck.Resources/RubberduckUI.Designer.cs +++ b/Rubberduck.Resources/RubberduckUI.Designer.cs @@ -1348,6 +1348,15 @@ public class RubberduckUI { } } + /// + /// Looks up a localized string similar to Instancing. + /// + public static string ExtractInterface_InstancingGroupBox { + get { + return ResourceManager.GetString("ExtractInterface_InstancingGroupBox", resourceCulture); + } + } + /// /// Looks up a localized string similar to Please specify interface name and members.. /// @@ -1366,6 +1375,24 @@ public class RubberduckUI { } } + /// + /// Looks up a localized string similar to Private. + /// + public static string ExtractInterface_PrivateRadioButton { + get { + return ResourceManager.GetString("ExtractInterface_PrivateRadioButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Public Not Creatable. + /// + public static string ExtractInterface_PublicNotCreatableRadioButton { + get { + return ResourceManager.GetString("ExtractInterface_PublicNotCreatableRadioButton", resourceCulture); + } + } + /// /// Looks up a localized string similar to Extract Interface. /// diff --git a/Rubberduck.Resources/RubberduckUI.resx b/Rubberduck.Resources/RubberduckUI.resx index a28f453774..f5b2c96354 100644 --- a/Rubberduck.Resources/RubberduckUI.resx +++ b/Rubberduck.Resources/RubberduckUI.resx @@ -1754,4 +1754,13 @@ Import aborted. VB Form + + Instancing + + + Private + + + Public Not Creatable + \ No newline at end of file diff --git a/Rubberduck.VBEditor.VBA/SafeComWrappers/Office/CommandBarButton.cs b/Rubberduck.VBEditor.VBA/SafeComWrappers/Office/CommandBarButton.cs index a35f769281..c9b56b4b7c 100644 --- a/Rubberduck.VBEditor.VBA/SafeComWrappers/Office/CommandBarButton.cs +++ b/Rubberduck.VBEditor.VBA/SafeComWrappers/Office/CommandBarButton.cs @@ -3,7 +3,6 @@ using System.Runtime.InteropServices; using System.Windows.Forms; using Microsoft.CSharp.RuntimeBinder; -using NLog; using Rubberduck.VBEditor.SafeComWrappers.Abstract; using MSO = Microsoft.Office.Core; From 00922e1179307034f7ae58988bcf5b8d53932db2 Mon Sep 17 00:00:00 2001 From: IvenBach Date: Wed, 15 Jan 2020 15:20:57 -0800 Subject: [PATCH 03/82] Make property assignment explicit --- .../ExtractInterface/InterfaceMember.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Rubberduck.Refactorings/ExtractInterface/InterfaceMember.cs b/Rubberduck.Refactorings/ExtractInterface/InterfaceMember.cs index 3817516ebe..4deb49b8b2 100644 --- a/Rubberduck.Refactorings/ExtractInterface/InterfaceMember.cs +++ b/Rubberduck.Refactorings/ExtractInterface/InterfaceMember.cs @@ -56,7 +56,7 @@ public InterfaceMember(Declaration member) Identifier = member.IdentifierName; Type = member.AsTypeName; - GetMethodType(); + MemberType = GetMethodType(Member.Context); if (member is IParameterizedDeclaration memberWithParams) { @@ -83,34 +83,34 @@ public InterfaceMember(Declaration member) } } - private void GetMethodType() + private string GetMethodType(Antlr4.Runtime.ParserRuleContext context) { - var context = Member.Context; - if (context is VBAParser.SubStmtContext) { - MemberType = Tokens.Sub; + return Tokens.Sub; } if (context is VBAParser.FunctionStmtContext) { - MemberType = Tokens.Function; + return Tokens.Function; } if (context is VBAParser.PropertyGetStmtContext) { - MemberType = $"{Tokens.Property} {Tokens.Get}"; + return $"{Tokens.Property} {Tokens.Get}"; } if (context is VBAParser.PropertyLetStmtContext) { - MemberType = $"{Tokens.Property} {Tokens.Let}"; + return $"{Tokens.Property} {Tokens.Let}"; } if (context is VBAParser.PropertySetStmtContext) { - MemberType = $"{Tokens.Property} {Tokens.Set}"; + return $"{Tokens.Property} {Tokens.Set}"; } + + return null; } public string Body => string.Format("Public {0}{1}End {2}{1}", FullMemberSignature, Environment.NewLine, MemberType.Split(' ').First()); From 5b09c23c667ac0a29564399be933864807509400 Mon Sep 17 00:00:00 2001 From: IvenBach Date: Wed, 15 Jan 2020 16:28:09 -0800 Subject: [PATCH 04/82] Add converter and use it --- .../ClassInstancingToBooleanConverter.cs | 26 +++++++++++++++++++ .../ExtractInterfaceView.xaml | 8 +++--- .../ExtractInterfaceViewModel.cs | 25 +++++++++++++++++- 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs diff --git a/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs b/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs new file mode 100644 index 0000000000..209aa0e519 --- /dev/null +++ b/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using Rubberduck.UI.Refactorings; + +namespace Rubberduck.UI.Converters +{ + class ClassInstancingToBooleanConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return (ClassInstancing)value == ClassInstancing.Private; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return (bool)value + ? ClassInstancing.Private + : ClassInstancing.PublicNotCreatable; + } + } +} diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml index 75b9cb3ae7..04da0e6aa2 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml @@ -7,6 +7,7 @@ mc:Ignorable="d" Height="378" Width="372"> + @@ -93,10 +94,11 @@ - + + IsEnabled="{Binding IsInterfacePublicNotCreateableEnabled}" /> diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs index 734172d181..4409601157 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs @@ -10,6 +10,12 @@ namespace Rubberduck.UI.Refactorings { + public enum ClassInstancing + { + Private = 0, + PublicNotCreatable, + } + public class ExtractInterfaceViewModel : RefactoringViewModelBase { public ExtractInterfaceViewModel(ExtractInterfaceModel model) : base(model) @@ -61,7 +67,7 @@ public bool IsValidInterfaceName } } - public bool IsInterfacePublicNotCreateable + public bool IsInterfacePublicNotCreateableEnabled { get { @@ -76,6 +82,23 @@ public bool IsInterfacePublicNotCreateable } } + private ClassInstancing classInstancing = ClassInstancing.Private; + public ClassInstancing ClassInstancing + { + get => classInstancing; + set + { + if (value == classInstancing) + { + return; + } + + classInstancing = value; + OnPropertyChanged(); + } + } + + private void ToggleSelection(bool value) { foreach (var item in Members) From 38d4399901f941928b6f61752efb257680a0ee74 Mon Sep 17 00:00:00 2001 From: IvenBach Date: Thu, 16 Jan 2020 13:34:57 -0800 Subject: [PATCH 05/82] Enable adding exposed attribute to interface Presently the choice is PublicNotCreatable if the implementing class is exposed. Next step is to connect view model RadioButton choice to determine the instacing. --- .../ClassInstancingToBooleanConverter.cs | 6 +-- .../ExtractInterfaceViewModel.cs | 6 --- .../ExtractInterface/ExtractInterfaceModel.cs | 7 ++++ .../ExtractInterfaceRefactoring.cs | 38 ++++++++++++++++--- 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs b/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs index 209aa0e519..f977865b9f 100644 --- a/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs +++ b/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Data; -using Rubberduck.UI.Refactorings; +using Rubberduck.Refactorings.ExtractInterface; namespace Rubberduck.UI.Converters { diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs index 4409601157..302ce36026 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs @@ -10,12 +10,6 @@ namespace Rubberduck.UI.Refactorings { - public enum ClassInstancing - { - Private = 0, - PublicNotCreatable, - } - public class ExtractInterfaceViewModel : RefactoringViewModelBase { public ExtractInterfaceViewModel(ExtractInterfaceModel model) : base(model) diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs index 4ccd4cca88..d7bed42a7d 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs @@ -6,6 +6,12 @@ namespace Rubberduck.Refactorings.ExtractInterface { + public enum ClassInstancing + { + Private = 0, + PublicNotCreatable, + } + public class ExtractInterfaceModel : IRefactoringModel { public IDeclarationFinderProvider DeclarationFinderProvider { get; } @@ -14,6 +20,7 @@ public class ExtractInterfaceModel : IRefactoringModel public string InterfaceName { get; set; } public ObservableCollection Members { get; set; } = new ObservableCollection(); public IEnumerable SelectedMembers => Members.Where(m => m.IsSelected); + public ClassInstancing ClassInstancing { get; set; } public static readonly DeclarationType[] MemberTypes = { diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs index 98c2ee5f4d..0cf1715e74 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs @@ -13,6 +13,7 @@ using Rubberduck.VBEditor; using Rubberduck.VBEditor.SafeComWrappers; using Rubberduck.VBEditor.Utility; +using Rubberduck.VBEditor.SafeComWrappers.Abstract; namespace Rubberduck.Refactorings.ExtractInterface { @@ -127,27 +128,52 @@ private void AddInterfaceClass(Declaration implementingClass, string interfaceNa interfaceComponent.Name = interfaceName; var optionPresent = interfaceModule.CountOfLines > 1; + var optionExplicit = $"{Tokens.Option} {Tokens.Explicit}{Environment.NewLine}"; if (!optionPresent) { - interfaceModule.InsertLines(1, $"{Tokens.Option} {Tokens.Explicit}{Environment.NewLine}"); + interfaceModule.InsertLines(1, optionExplicit); } + interfaceModule.InsertLines(3, interfaceBody); + + var classIsExposed = Convert.ToBoolean(implementingClass.Attributes.ExposedAttribute.Values.First()); + if (classIsExposed) + { + AddExposedAttribute(components, interfaceComponent); + } } } } } - private void AddInterfaceMembersToClass(ExtractInterfaceModel model, IModuleRewriter rewriter) + private void AddExposedAttribute(IVBComponents components, IVBComponent interfaceComponent) { - _implementInterfaceRefactoring.Refactor(model.SelectedMembers.Select(m => m.Member).ToList(), rewriter, model.InterfaceName); + try + { + var tempExportDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + System.IO.Path.DirectorySeparatorChar + "Rubberduck" + System.IO.Path.DirectorySeparatorChar; + var tempFile = interfaceComponent.ExportAsSourceFile(tempExportDirectory + interfaceComponent.Name); + + var text = System.IO.File.ReadAllText(tempFile); + var sb = new System.Text.StringBuilder(text); + sb.Replace("Attribute VB_Exposed = False", "Attribute VB_Exposed = True"); + System.IO.File.WriteAllText(tempFile, sb.ToString()); - var classIsExposed = Convert.ToBoolean(model.TargetDeclaration.Attributes.ExposedAttribute.Values.First()); - if (classIsExposed) + components.Remove(interfaceComponent); + components.ImportSourceFile(tempFile); + + System.IO.File.Delete(tempFile); + } + catch (Exception) { - model.TargetDeclaration.Attributes.AddExposedClassAttribute(); + throw; } } + private void AddInterfaceMembersToClass(ExtractInterfaceModel model, IModuleRewriter rewriter) + { + _implementInterfaceRefactoring.Refactor(model.SelectedMembers.Select(m => m.Member).ToList(), rewriter, model.InterfaceName); + } + private string GetInterfaceModuleBody(ExtractInterfaceModel model) { return string.Join(Environment.NewLine, model.SelectedMembers.Select(m => m.Body)); From 3b63eb9c311a4cc4d3bf4580b127cb9791a800c4 Mon Sep 17 00:00:00 2001 From: IvenBach Date: Fri, 17 Jan 2020 12:52:10 -0800 Subject: [PATCH 06/82] Update extract public interface A private class can have a (public | private) interface extracted. The default option is public. When an implementing class is public the newly created interface must also too be public. As such the private option for the extracted interface is disabled. Need to get the Public.Checked = true on private implementing classes. Once that's done I can merge the following commits together and edit this commit. --- .../ClassInstancingToBooleanConverter.cs | 6 ++-- .../ExtractInterfaceView.xaml | 11 ++++---- .../ExtractInterfaceViewModel.cs | 28 ++++++++----------- .../ExtractInterface/ExtractInterfaceModel.cs | 5 +++- .../ExtractInterfaceRefactoring.cs | 14 +++++----- Rubberduck.Resources/RubberduckUI.Designer.cs | 2 +- Rubberduck.Resources/RubberduckUI.resx | 2 +- 7 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs b/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs index f977865b9f..25f3fcb6ba 100644 --- a/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs +++ b/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs @@ -9,14 +9,14 @@ class ClassInstancingToBooleanConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - return (ClassInstancing)value == ClassInstancing.Private; + return (ClassInstancing)value == ClassInstancing.PublicNotCreatable; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return (bool)value - ? ClassInstancing.Private - : ClassInstancing.PublicNotCreatable; + ? ClassInstancing.PublicNotCreatable + : ClassInstancing.Private; } } } diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml index 04da0e6aa2..cfc0292cc1 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml @@ -8,6 +8,7 @@ + @@ -93,12 +94,12 @@ - - + + + IsChecked="{Binding IsPublicInterfaceChecked, Mode=TwoWay}" /> diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs index 302ce36026..2ff79ac9d6 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs @@ -7,6 +7,7 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings.ExtractInterface; using Rubberduck.UI.Command; +using System.Windows.Data; namespace Rubberduck.UI.Refactorings { @@ -61,38 +62,33 @@ public bool IsValidInterfaceName } } - public bool IsInterfacePublicNotCreateableEnabled + public bool IsPrivateInterfaceEnabled { get { - try - { - return Convert.ToBoolean(Model.TargetDeclaration.Attributes.ExposedAttribute.Values.First()); - } - catch (FormatException) - { - return false; - } + return Model.ImplementingClassInstancing != ClassInstancing.PublicNotCreatable; } } - private ClassInstancing classInstancing = ClassInstancing.Private; - public ClassInstancing ClassInstancing + private readonly IValueConverter classInstancingConverter = new Converters.ClassInstancingToBooleanConverter(); + + private bool isPublicInterfaceChecked = true; + public bool IsPublicInterfaceChecked { - get => classInstancing; + get => isPublicInterfaceChecked; set { - if (value == classInstancing) + if (value == isPublicInterfaceChecked) { return; } - classInstancing = value; - OnPropertyChanged(); + Model.ImplementingClassInstancing = (ClassInstancing)classInstancingConverter.ConvertBack(value, null, null, null); + isPublicInterfaceChecked = value; + OnPropertyChanged(); } } - private void ToggleSelection(bool value) { foreach (var item in Members) diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs index d7bed42a7d..a40272af48 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs @@ -20,7 +20,7 @@ public class ExtractInterfaceModel : IRefactoringModel public string InterfaceName { get; set; } public ObservableCollection Members { get; set; } = new ObservableCollection(); public IEnumerable SelectedMembers => Members.Where(m => m.IsSelected); - public ClassInstancing ClassInstancing { get; set; } + public ClassInstancing ImplementingClassInstancing { get; set; } public static readonly DeclarationType[] MemberTypes = { @@ -35,6 +35,9 @@ public ExtractInterfaceModel(IDeclarationFinderProvider declarationFinderProvide { TargetDeclaration = target; DeclarationFinderProvider = declarationFinderProvider; + ImplementingClassInstancing = System.Convert.ToBoolean(target.Attributes.ExposedAttribute.Values.First()) + ? ClassInstancing.PublicNotCreatable + : ClassInstancing.Private; if (TargetDeclaration == null) { diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs index 0cf1715e74..f723fdbb4d 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs @@ -98,7 +98,7 @@ private void AddInterface(ExtractInterfaceModel model) return; //The target project is not available. } - AddInterfaceClass(model.TargetDeclaration, model.InterfaceName, GetInterfaceModuleBody(model)); + AddInterfaceClass(model.TargetDeclaration, model.InterfaceName, GetInterfaceModuleBody(model), model.ImplementingClassInstancing); var rewriteSession = RewritingManager.CheckOutCodePaneSession(); var rewriter = rewriteSession.CheckOutModuleRewriter(model.TargetDeclaration.QualifiedModuleName); @@ -116,7 +116,7 @@ private void AddInterface(ExtractInterfaceModel model) } } - private void AddInterfaceClass(Declaration implementingClass, string interfaceName, string interfaceBody) + private void AddInterfaceClass(Declaration implementingClass, string interfaceName, string interfaceBody, ClassInstancing interfaceInstancing) { var targetProject = implementingClass.Project; using (var components = targetProject.VBComponents) @@ -128,16 +128,15 @@ private void AddInterfaceClass(Declaration implementingClass, string interfaceNa interfaceComponent.Name = interfaceName; var optionPresent = interfaceModule.CountOfLines > 1; - var optionExplicit = $"{Tokens.Option} {Tokens.Explicit}{Environment.NewLine}"; if (!optionPresent) { - interfaceModule.InsertLines(1, optionExplicit); + interfaceModule.InsertLines(1, "Option Explicit" + Environment.NewLine); } - interfaceModule.InsertLines(3, interfaceBody); + interfaceModule.InsertLines(1, "'@Interface"); + interfaceModule.InsertLines(4, interfaceBody); - var classIsExposed = Convert.ToBoolean(implementingClass.Attributes.ExposedAttribute.Values.First()); - if (classIsExposed) + if (interfaceInstancing == ClassInstancing.PublicNotCreatable) { AddExposedAttribute(components, interfaceComponent); } @@ -155,6 +154,7 @@ private void AddExposedAttribute(IVBComponents components, IVBComponent interfac var text = System.IO.File.ReadAllText(tempFile); var sb = new System.Text.StringBuilder(text); + sb.Insert(text.IndexOf("Option Explicit"), "'@Exposed" + Environment.NewLine); sb.Replace("Attribute VB_Exposed = False", "Attribute VB_Exposed = True"); System.IO.File.WriteAllText(tempFile, sb.ToString()); diff --git a/Rubberduck.Resources/RubberduckUI.Designer.cs b/Rubberduck.Resources/RubberduckUI.Designer.cs index 735c1b7314..c6bf02ae9b 100644 --- a/Rubberduck.Resources/RubberduckUI.Designer.cs +++ b/Rubberduck.Resources/RubberduckUI.Designer.cs @@ -1385,7 +1385,7 @@ public class RubberduckUI { } /// - /// Looks up a localized string similar to Public Not Creatable. + /// Looks up a localized string similar to Public (Not Creatable). /// public static string ExtractInterface_PublicNotCreatableRadioButton { get { diff --git a/Rubberduck.Resources/RubberduckUI.resx b/Rubberduck.Resources/RubberduckUI.resx index f5b2c96354..54aa6cedd7 100644 --- a/Rubberduck.Resources/RubberduckUI.resx +++ b/Rubberduck.Resources/RubberduckUI.resx @@ -1761,6 +1761,6 @@ Import aborted. Private - Public Not Creatable + Public (Not Creatable) \ No newline at end of file From e8fd5737feb1b146a0d2d4267ed20f7976c60611 Mon Sep 17 00:00:00 2001 From: IvenBach Date: Fri, 17 Jan 2020 13:57:12 -0800 Subject: [PATCH 07/82] Opportunistic updates Use pattern matching Simplify default expression Use auto property Use IEnumerable. ToList() removed as ubsequent calls use IEnumerable. --- .../UI/Command/MenuItems/CommandMenuItemBase.cs | 2 +- .../MenuItems/ParentMenus/ParentMenuItemBase.cs | 3 +-- Rubberduck.Parsing/VBA/RubberduckParserState.cs | 15 +++++++-------- .../ExtractInterfaceRefactoring.cs | 2 +- .../ImplementInterfaceRefactoring.cs | 2 +- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Rubberduck.Core/UI/Command/MenuItems/CommandMenuItemBase.cs b/Rubberduck.Core/UI/Command/MenuItems/CommandMenuItemBase.cs index 6271d4ae8a..d2acfb9fb7 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/CommandMenuItemBase.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/CommandMenuItemBase.cs @@ -54,7 +54,7 @@ public virtual bool EvaluateCanExecute(RubberduckParserState state) public virtual bool HiddenWhenDisabled => false; public virtual bool IsVisible => true; public virtual bool BeginGroup => false; - public virtual int DisplayOrder => default(int); + public virtual int DisplayOrder => default; public virtual Image Image => null; public virtual Image Mask => null; } diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs index 5047c33265..9e231ebb4e 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs @@ -201,8 +201,7 @@ private ICommandBarControl InitializeChildControl(ICommandMenuItem item) private void child_Click(object sender, CommandBarButtonClickEventArgs e) { - var item = _items.Select(kvp => kvp.Key).SingleOrDefault(menu => e.Tag.EndsWith(menu.GetType().Name)) as ICommandMenuItem; - if (item == null) + if (!(_items.Select(kvp => kvp.Key).SingleOrDefault(menu => e.Tag.EndsWith(menu.GetType().Name)) is ICommandMenuItem item)) { return; } diff --git a/Rubberduck.Parsing/VBA/RubberduckParserState.cs b/Rubberduck.Parsing/VBA/RubberduckParserState.cs index fcb54a2871..d80d4de17a 100644 --- a/Rubberduck.Parsing/VBA/RubberduckParserState.cs +++ b/Rubberduck.Parsing/VBA/RubberduckParserState.cs @@ -581,17 +581,16 @@ public ParserState GetModuleState(QualifiedModuleName module) return _moduleStates.GetOrAdd(module, new ModuleState(ParserState.Pending)).State; } - private readonly object _statusLockObject = new object(); - private ParserState _status; - public ParserState Status => _status; + private readonly object _statusLockObject = new object(); + public ParserState Status { get; private set; } private void SetStatusWithCancellation(ParserState value, CancellationToken token) { - if (_status != value) + if (Status != value) { - var oldStatus = _status; - _status = value; - OnStateChanged(this, token, _status, oldStatus); + var oldStatus = Status; + Status = value; + OnStateChanged(this, token, Status, oldStatus); } } @@ -599,7 +598,7 @@ public void SetStatusAndFireStateChanged(object requestor, ParserState status, C { if (Status == status) { - OnStateChanged(requestor, token, status, _status); + OnStateChanged(requestor, token, status, Status); } else { diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs index f723fdbb4d..0d00e4b03b 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs @@ -171,7 +171,7 @@ private void AddExposedAttribute(IVBComponents components, IVBComponent interfac private void AddInterfaceMembersToClass(ExtractInterfaceModel model, IModuleRewriter rewriter) { - _implementInterfaceRefactoring.Refactor(model.SelectedMembers.Select(m => m.Member).ToList(), rewriter, model.InterfaceName); + _implementInterfaceRefactoring.Refactor(model.SelectedMembers.Select(m => m.Member), rewriter, model.InterfaceName); } private string GetInterfaceModuleBody(ExtractInterfaceModel model) diff --git a/Rubberduck.Refactorings/ImplementInterface/ImplementInterfaceRefactoring.cs b/Rubberduck.Refactorings/ImplementInterface/ImplementInterfaceRefactoring.cs index 0a80552b8b..a24a003d38 100644 --- a/Rubberduck.Refactorings/ImplementInterface/ImplementInterfaceRefactoring.cs +++ b/Rubberduck.Refactorings/ImplementInterface/ImplementInterfaceRefactoring.cs @@ -79,7 +79,7 @@ public override void Refactor(Declaration target) throw new NotSupportedException(); } - internal void Refactor(List members, IModuleRewriter rewriter, string interfaceName) + internal void Refactor(IEnumerable members, IModuleRewriter rewriter, string interfaceName) { AddItems(members, rewriter, interfaceName); } From 664c7d995e1c4de12e07e4a30696bc6f50eed19c Mon Sep 17 00:00:00 2001 From: IvenBach Date: Fri, 17 Jan 2020 14:47:05 -0800 Subject: [PATCH 08/82] Correct default public interface extraction --- .../ExtractInterface/ExtractInterfaceViewModel.cs | 7 +++---- .../ExtractInterface/ExtractInterfaceModel.cs | 8 ++++---- .../ExtractInterface/ExtractInterfaceRefactoring.cs | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs index 2ff79ac9d6..599ffbb8a9 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs @@ -7,7 +7,6 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings.ExtractInterface; using Rubberduck.UI.Command; -using System.Windows.Data; namespace Rubberduck.UI.Refactorings { @@ -70,8 +69,6 @@ public bool IsPrivateInterfaceEnabled } } - private readonly IValueConverter classInstancingConverter = new Converters.ClassInstancingToBooleanConverter(); - private bool isPublicInterfaceChecked = true; public bool IsPublicInterfaceChecked { @@ -83,7 +80,9 @@ public bool IsPublicInterfaceChecked return; } - Model.ImplementingClassInstancing = (ClassInstancing)classInstancingConverter.ConvertBack(value, null, null, null); + Model.InterfaceInstancing = value + ? ClassInstancing.PublicNotCreatable + : ClassInstancing.Private; isPublicInterfaceChecked = value; OnPropertyChanged(); } diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs index a40272af48..32da869aeb 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs @@ -20,7 +20,10 @@ public class ExtractInterfaceModel : IRefactoringModel public string InterfaceName { get; set; } public ObservableCollection Members { get; set; } = new ObservableCollection(); public IEnumerable SelectedMembers => Members.Where(m => m.IsSelected); - public ClassInstancing ImplementingClassInstancing { get; set; } + public ClassInstancing ImplementingClassInstancing => System.Convert.ToBoolean(TargetDeclaration.Attributes.ExposedAttribute.Values.First()) + ? ClassInstancing.PublicNotCreatable + : ClassInstancing.Private; + public ClassInstancing InterfaceInstancing { get; set; } = ClassInstancing.PublicNotCreatable; public static readonly DeclarationType[] MemberTypes = { @@ -35,9 +38,6 @@ public ExtractInterfaceModel(IDeclarationFinderProvider declarationFinderProvide { TargetDeclaration = target; DeclarationFinderProvider = declarationFinderProvider; - ImplementingClassInstancing = System.Convert.ToBoolean(target.Attributes.ExposedAttribute.Values.First()) - ? ClassInstancing.PublicNotCreatable - : ClassInstancing.Private; if (TargetDeclaration == null) { diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs index 0d00e4b03b..4e0f95e0b0 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs @@ -98,7 +98,7 @@ private void AddInterface(ExtractInterfaceModel model) return; //The target project is not available. } - AddInterfaceClass(model.TargetDeclaration, model.InterfaceName, GetInterfaceModuleBody(model), model.ImplementingClassInstancing); + AddInterfaceClass(model.TargetDeclaration, model.InterfaceName, GetInterfaceModuleBody(model), model.InterfaceInstancing); var rewriteSession = RewritingManager.CheckOutCodePaneSession(); var rewriter = rewriteSession.CheckOutModuleRewriter(model.TargetDeclaration.QualifiedModuleName); From d0a341a4caa268393d803b25fa9274fbbdc872a4 Mon Sep 17 00:00:00 2001 From: IvenBach Date: Tue, 21 Jan 2020 15:50:54 -0800 Subject: [PATCH 09/82] Update instancing selection to use combobox --- .../ClassInstancingToBooleanConverter.cs | 22 ----------------- .../ExtractInterfaceView.xaml | 20 +++++++++------- .../ExtractInterfaceViewModel.cs | 24 ++++++++++++++++--- .../ExtractInterface/ExtractInterfaceModel.cs | 8 +++---- .../ExtractInterfaceRefactoring.cs | 2 +- Rubberduck.Resources/RubberduckUI.Designer.cs | 19 +++++++++++---- Rubberduck.Resources/RubberduckUI.resx | 9 ++++--- 7 files changed, 57 insertions(+), 47 deletions(-) delete mode 100644 Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs diff --git a/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs b/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs deleted file mode 100644 index 25f3fcb6ba..0000000000 --- a/Rubberduck.Core/UI/Converters/ClassInstancingToBooleanConverter.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; -using Rubberduck.Refactorings.ExtractInterface; - -namespace Rubberduck.UI.Converters -{ - class ClassInstancingToBooleanConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return (ClassInstancing)value == ClassInstancing.PublicNotCreatable; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return (bool)value - ? ClassInstancing.PublicNotCreatable - : ClassInstancing.Private; - } - } -} diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml index a3c9ea1345..78da578771 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml @@ -13,7 +13,8 @@ - + + @@ -51,16 +52,17 @@ VerticalAlignment="Top" Visibility="{Binding IsValidInterfaceName, Converter={StaticResource BoolToHiddenVisibility}}"/> - + - - - - + + + + ClassInstances => Enum.GetValues(typeof(ClassInstancing)).Cast(); + + public ClassInstancing InterfaceInstancing + { + get => Model.InterfaceInstancing; + + set + { + if (value == Model.InterfaceInstancing) + { + return; + } + + Model.InterfaceInstancing = value; + OnPropertyChanged(); } } @@ -81,7 +99,7 @@ public bool IsPublicInterfaceChecked } Model.InterfaceInstancing = value - ? ClassInstancing.PublicNotCreatable + ? ClassInstancing.Public : ClassInstancing.Private; isPublicInterfaceChecked = value; OnPropertyChanged(); diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs index 32da869aeb..579d350565 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs @@ -8,8 +8,8 @@ namespace Rubberduck.Refactorings.ExtractInterface { public enum ClassInstancing { - Private = 0, - PublicNotCreatable, + Public = 0, + Private } public class ExtractInterfaceModel : IRefactoringModel @@ -21,9 +21,9 @@ public class ExtractInterfaceModel : IRefactoringModel public ObservableCollection Members { get; set; } = new ObservableCollection(); public IEnumerable SelectedMembers => Members.Where(m => m.IsSelected); public ClassInstancing ImplementingClassInstancing => System.Convert.ToBoolean(TargetDeclaration.Attributes.ExposedAttribute.Values.First()) - ? ClassInstancing.PublicNotCreatable + ? ClassInstancing.Public : ClassInstancing.Private; - public ClassInstancing InterfaceInstancing { get; set; } = ClassInstancing.PublicNotCreatable; + public ClassInstancing InterfaceInstancing { get; set; } = ClassInstancing.Public; public static readonly DeclarationType[] MemberTypes = { diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs index 4e0f95e0b0..a8daa8a861 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs @@ -136,7 +136,7 @@ private void AddInterfaceClass(Declaration implementingClass, string interfaceNa interfaceModule.InsertLines(1, "'@Interface"); interfaceModule.InsertLines(4, interfaceBody); - if (interfaceInstancing == ClassInstancing.PublicNotCreatable) + if (interfaceInstancing == ClassInstancing.Public) { AddExposedAttribute(components, interfaceComponent); } diff --git a/Rubberduck.Resources/RubberduckUI.Designer.cs b/Rubberduck.Resources/RubberduckUI.Designer.cs index f194568293..8b6a939fdd 100644 --- a/Rubberduck.Resources/RubberduckUI.Designer.cs +++ b/Rubberduck.Resources/RubberduckUI.Designer.cs @@ -1450,18 +1450,27 @@ public class RubberduckUI { /// /// Looks up a localized string similar to Private. /// - public static string ExtractInterface_PrivateRadioButton { + public static string ExtractInterface_Private { get { - return ResourceManager.GetString("ExtractInterface_PrivateRadioButton", resourceCulture); + return ResourceManager.GetString("ExtractInterface_Private", resourceCulture); } } /// - /// Looks up a localized string similar to Public (Not Creatable). + /// Looks up a localized string similar to Public. /// - public static string ExtractInterface_PublicNotCreatableRadioButton { + public static string ExtractInterface_Public { get { - return ResourceManager.GetString("ExtractInterface_PublicNotCreatableRadioButton", resourceCulture); + return ResourceManager.GetString("ExtractInterface_Public", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 'Public' will be chosen. For private instancing make implementing class private also.. + /// + public static string ExtractInterface_PublicInstancingMandatedByPublicClass { + get { + return ResourceManager.GetString("ExtractInterface_PublicInstancingMandatedByPublicClass", resourceCulture); } } diff --git a/Rubberduck.Resources/RubberduckUI.resx b/Rubberduck.Resources/RubberduckUI.resx index e2b28f1a2a..aae8aaef0f 100644 --- a/Rubberduck.Resources/RubberduckUI.resx +++ b/Rubberduck.Resources/RubberduckUI.resx @@ -1782,10 +1782,13 @@ Import aborted. Instancing - + Private - - Public (Not Creatable) + + Public + + + 'Public' will be chosen. For private instancing make implementing class private also. \ No newline at end of file From 436ec4dd4eb8ee59ccbf9276ad16097febd23a6e Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 1 Apr 2020 18:53:59 -0700 Subject: [PATCH 10/82] Initial implementation Introduces ICodeBuilder and CodeBuilder in place of extension methods. --- Rubberduck.Refactorings/Common/CodeBuilder.cs | 284 ++++++++++++ .../Common/DeclarationExtensions.cs | 61 --- .../ModuleBodyElementDeclarationExtensions.cs | 176 -------- .../EncapsulateFieldRefactoring.cs | 9 +- .../ConvertFieldsToUDTMembers.cs | 38 +- .../EncapsulateFieldStrategyBase.cs | 84 ++-- .../UseBackingFields.cs | 4 +- .../ExtractInterface/ExtractInterfaceModel.cs | 8 +- .../ExtractInterfaceRefactoring.cs | 7 +- .../ExtractInterface/InterfaceMember.cs | 13 +- ...terfaceImplementationsRefactoringAction.cs | 30 +- RubberduckTests/CodeBuilderTests.cs | 413 ++++++++++++++++++ .../CodeExplorer/MockedCodeExplorer.cs | 5 +- .../EncapsulateFieldCommandTests.cs | 2 +- .../ExtractInterfaceCommandTests.cs | 4 +- .../ImplementInterfaceCommandTests.cs | 3 +- .../EncapsulateFIeldTestSupport.cs | 2 +- .../ExtractInterfaceRefactoringActionTests.cs | 4 +- .../ExtractInterface/ExtractInterfaceTests.cs | 10 +- ...mplementInterfaceRefactoringActionTests.cs | 2 +- .../ImplementInterfaceTests.cs | 2 +- 21 files changed, 814 insertions(+), 347 deletions(-) create mode 100644 Rubberduck.Refactorings/Common/CodeBuilder.cs delete mode 100644 Rubberduck.Refactorings/Common/ModuleBodyElementDeclarationExtensions.cs create mode 100644 RubberduckTests/CodeBuilderTests.cs diff --git a/Rubberduck.Refactorings/Common/CodeBuilder.cs b/Rubberduck.Refactorings/Common/CodeBuilder.cs new file mode 100644 index 0000000000..3e14a75c5b --- /dev/null +++ b/Rubberduck.Refactorings/Common/CodeBuilder.cs @@ -0,0 +1,284 @@ +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Symbols; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings +{ + public interface ICodeBuilder + { + /// + /// Returns ModuleBodyElementDeclaration signature with an ImprovedArgument list + /// + /// + /// + string ImprovedFullMemberSignature(ModuleBodyElementDeclaration declaration); + + /// + /// Returns a ModuleBodyElementDeclaration block + /// with an ImprovedArgument List + /// + /// + /// Main body content/logic of the member + /// + /// + /// + string BuildMemberBlockFromPrototype(ModuleBodyElementDeclaration declaration, + string content = null, + string accessibility = null, + string newIdentifier = null); + + /// + /// Returns the argument list for the input ModuleBodyElementDeclaration with the following improvements: + /// 1. Explicitly declares Property Let\Set value parameter as ByVal + /// 2. Ensures UserDefined Type parameters are declared either explicitly or implicitly as ByRef + /// + /// + /// + string ImprovedArgumentList(ModuleBodyElementDeclaration declaration); + + /// + /// Generates a Property Get codeblock based on the prototype declaration + /// + /// VariableDeclaration or UserDefinedTypeMember + /// + /// + /// Member body content. Formatting is the responsibility of the caller + /// Defaults to 'value' unless otherwise specified + /// + bool TryBuildPropertyGetCodeBlock(Declaration prototype, + string propertyIdentifier, + out string codeBlock, + string accessibility = null, + string content = null); + + /// + /// Generates a Property Let codeblock based on the prototype declaration + /// + /// VariableDeclaration or UserDefinedTypeMember + /// + /// + /// Membmer body content. Formatting is the responsibility of the caller + /// Defaults to 'value' unless otherwise specified + /// + bool TryBuildPropertyLetCodeBlock(Declaration prototype, + string propertyIdentifier, + out string codeBlock, + string accessibility = null, + string content = null, + string parameterIdentifier = null); + + /// + /// Generates a Property Set codeblock based on the prototype declaration + /// + /// VariableDeclaration or UserDefinedTypeMember + /// + /// + /// Membmer body content. Formatting is the responsibility of the caller + /// Defaults to 'value' unless otherwise specified + /// + bool TryBuildPropertySetCodeBlock(Declaration prototype, + string propertyIdentifier, + out string codeBlock, + string accessibility = null, + string content = null, + string parameterIdentifier = null); + } + + public class CodeBuilder : ICodeBuilder + { + public string BuildMemberBlockFromPrototype(ModuleBodyElementDeclaration declaration, + string content = null, + string accessibility = null, + string newIdentifier = null) + { + + var elements = new List() + { + ImprovedFullMemberSignatureInternal(declaration, accessibility, newIdentifier), + Environment.NewLine, + string.IsNullOrEmpty(content) ? null : $"{content}{Environment.NewLine}", + ProcedureEndStatement(declaration.DeclarationType), + Environment.NewLine, + }; + return string.Concat(elements); + } + + public bool TryBuildPropertyGetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null) + => TryBuildPropertyBlockFromTarget(prototype, DeclarationType.PropertyGet, propertyIdentifier, out codeBlock, accessibility, content); + + public bool TryBuildPropertyLetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null, string parameterIdentifier = null) + => TryBuildPropertyBlockFromTarget(prototype, DeclarationType.PropertyLet, propertyIdentifier, out codeBlock, accessibility, content, parameterIdentifier); + + public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null, string parameterIdentifier = null) + => TryBuildPropertyBlockFromTarget(prototype, DeclarationType.PropertySet, propertyIdentifier, out codeBlock, accessibility, content, parameterIdentifier); + + private bool TryBuildPropertyBlockFromTarget(T prototype, DeclarationType letSetGetType, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null, string parameterIdentifier = null) where T : Declaration + { + codeBlock = string.Empty; + if (!letSetGetType.HasFlag(DeclarationType.Property)) + { + throw new ArgumentException(); + } + + if (!(prototype is VariableDeclaration || prototype.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember))) + { + return false; + } + + var propertyValueParam = parameterIdentifier ?? Resources.RubberduckUI.EncapsulateField_DefaultPropertyParameter; + + var asType = prototype.IsArray + ? $"{Tokens.Variant}" + : IsEnumField(prototype) && prototype.AsTypeDeclaration.Accessibility.Equals(Accessibility.Private) + ? $"{Tokens.Long}" + : $"{prototype.AsTypeName}"; + + var asTypeClause = $"{Tokens.As} {asType}"; + + var paramMechanism = IsUserDefinedType(prototype) ? Tokens.ByRef : Tokens.ByVal; + + var letSetParamExpression = $"{paramMechanism} {propertyValueParam} {asTypeClause}"; + + codeBlock = letSetGetType.HasFlag(DeclarationType.PropertyGet) + ? string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {ProcedureTypeStatement(letSetGetType)} {propertyIdentifier}() {asTypeClause}", content, ProcedureEndStatement(letSetGetType)) + : string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {ProcedureTypeStatement(letSetGetType)} {propertyIdentifier}({letSetParamExpression})", content, ProcedureEndStatement(letSetGetType)); + return true; + } + + public string ImprovedFullMemberSignature(ModuleBodyElementDeclaration declaration) + => ImprovedFullMemberSignatureInternal(declaration); + + private string ImprovedFullMemberSignatureInternal(ModuleBodyElementDeclaration declaration, string accessibility = null, string newIdentifier = null) + { + var accessibilityToken = declaration.Accessibility.Equals(Accessibility.Implicit) + ? Tokens.Public + : $"{declaration.Accessibility.ToString()}"; + + var asTypeName = string.IsNullOrEmpty(declaration.AsTypeName) + ? string.Empty + : $" {Tokens.As} {declaration.AsTypeName}"; + + var elements = new List() + { + accessibility ?? accessibilityToken, + $" {ProcedureTypeStatement(declaration.DeclarationType)} ", + newIdentifier ?? declaration.IdentifierName, + $"({ImprovedArgumentList(declaration)})", + asTypeName + }; + return string.Concat(elements).Trim(); + + } + + public string ImprovedArgumentList(ModuleBodyElementDeclaration declaration) + { + var arguments = Enumerable.Empty(); + if (declaration is IParameterizedDeclaration parameterizedDeclaration) + { + arguments = parameterizedDeclaration.Parameters + .OrderBy(parameter => parameter.Selection) + .Select(parameter => BuildParameterDeclaration( + parameter, + parameter.Equals(parameterizedDeclaration.Parameters.LastOrDefault()) + && declaration.DeclarationType.HasFlag(DeclarationType.Property) + && !declaration.DeclarationType.Equals(DeclarationType.PropertyGet))); + } + return $"{string.Join(", ", arguments)}"; + } + + private static string BuildParameterDeclaration(ParameterDeclaration parameter, bool forceExplicitByValAccess) + { + var optionalParamType = parameter.IsParamArray + ? Tokens.ParamArray + : parameter.IsOptional ? Tokens.Optional : string.Empty; + + var paramMechanism = parameter.IsImplicitByRef + ? string.Empty + : parameter.IsByRef ? Tokens.ByRef : Tokens.ByVal; + + if (forceExplicitByValAccess + && (string.IsNullOrEmpty(paramMechanism) || paramMechanism.Equals(Tokens.ByRef)) + && !IsUserDefinedType(parameter)) + { + paramMechanism = Tokens.ByVal; + } + + var name = parameter.IsArray + ? $"{parameter.IdentifierName}()" + : parameter.IdentifierName; + + var paramDeclarationElements = new List() + { + FormatOptionalElement(optionalParamType), + FormatOptionalElement(paramMechanism), + $"{name} ", + FormatAsTypeName(parameter.AsTypeName), + FormatDefaultValue(parameter.DefaultValue) + }; + + return string.Concat(paramDeclarationElements).Trim(); + } + + private static string FormatOptionalElement(string element) + => string.IsNullOrEmpty(element) ? string.Empty : $"{element} "; + + private static string FormatAsTypeName(string AsTypeName) + => string.IsNullOrEmpty(AsTypeName) ? string.Empty : $"As {AsTypeName} "; + + private static string FormatDefaultValue(string DefaultValue) + => string.IsNullOrEmpty(DefaultValue) ? string.Empty : $"= {DefaultValue}"; + + private static string ProcedureEndStatement(DeclarationType declarationType) + { + switch (declarationType) + { + case DeclarationType.Function: + return $"{Tokens.End} {Tokens.Function}"; + case DeclarationType.Procedure: + return $"{Tokens.End} {Tokens.Sub}"; + case DeclarationType.PropertyGet: + case DeclarationType.PropertyLet: + case DeclarationType.PropertySet: + return $"{Tokens.End} {Tokens.Property}"; + default: + throw new ArgumentException(); + } + } + + private static string ProcedureTypeStatement(DeclarationType declarationType) + { + switch (declarationType) + { + case DeclarationType.Function: + return Tokens.Function; + case DeclarationType.Procedure: + return Tokens.Sub; + case DeclarationType.PropertyGet: + return $"{Tokens.Property} {Tokens.Get}"; + case DeclarationType.PropertyLet: + return $"{Tokens.Property} {Tokens.Let}"; + case DeclarationType.PropertySet: + return $"{Tokens.Property} {Tokens.Set}"; + default: + throw new ArgumentException(); + } + } + + private static bool IsEnumField(VariableDeclaration declaration) + => IsMemberVariable(declaration) + && (declaration.AsTypeDeclaration?.DeclarationType.Equals(DeclarationType.Enumeration) ?? false); + + private static bool IsEnumField(Declaration declaration) + => IsMemberVariable(declaration) + && (declaration.AsTypeDeclaration?.DeclarationType.Equals(DeclarationType.Enumeration) ?? false); + + private static bool IsUserDefinedType(Declaration declaration) + => (declaration.AsTypeDeclaration?.DeclarationType.Equals(DeclarationType.UserDefinedType) ?? false); + + private static bool IsMemberVariable(Declaration declaration) + => declaration.DeclarationType.HasFlag(DeclarationType.Variable) + && !declaration.ParentDeclaration.DeclarationType.HasFlag(DeclarationType.Member); + } +} diff --git a/Rubberduck.Refactorings/Common/DeclarationExtensions.cs b/Rubberduck.Refactorings/Common/DeclarationExtensions.cs index 4e9f7b24c2..2c8519b8e8 100644 --- a/Rubberduck.Refactorings/Common/DeclarationExtensions.cs +++ b/Rubberduck.Refactorings/Common/DeclarationExtensions.cs @@ -1,10 +1,6 @@ using Rubberduck.Parsing; using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Symbols; -using Rubberduck.Refactorings.Common; -using System; -using System.Collections.Generic; -using System.Linq; namespace Rubberduck.Refactorings.Common { @@ -45,62 +41,5 @@ public static bool IsDeclaredInList(this Declaration declaration) return declaration.Context.TryGetAncestor(out var varList) && varList.ChildCount > 1; } - - /// - /// Generates a Property Member code block specified by the letSetGet DeclarationType argument. - /// - /// - /// - /// - /// - /// - /// - /// - public static string FieldToPropertyBlock(this Declaration variable, DeclarationType letSetGetType, string propertyIdentifier, string accessibility = null, string content = null, string parameterIdentifier = null) - { - //"value" is the default - var propertyValueParam = parameterIdentifier ?? Resources.RubberduckUI.EncapsulateField_DefaultPropertyParameter; - - var propertyEndStmt = $"{Tokens.End} {Tokens.Property}"; - - var asType = variable.IsArray - ? $"{Tokens.Variant}" - : variable.IsEnumField() && variable.AsTypeDeclaration.HasPrivateAccessibility() - ? $"{Tokens.Long}" - : $"{variable.AsTypeName}"; - - var asTypeClause = $"{Tokens.As} {asType}"; - - var paramAccessibility = variable.IsUserDefinedType() ? Tokens.ByRef : Tokens.ByVal; - - var letSetParameter = $"{paramAccessibility} {propertyValueParam} {Tokens.As} {asType}"; - - switch (letSetGetType) - { - case DeclarationType.PropertyGet: - return string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {PropertyTypeStatement(letSetGetType)} {propertyIdentifier}() {asTypeClause}", content, propertyEndStmt); - case DeclarationType.PropertyLet: - case DeclarationType.PropertySet: - return string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {PropertyTypeStatement(letSetGetType)} {propertyIdentifier}({letSetParameter})", content, propertyEndStmt); - default: - throw new ArgumentException(); - } - } - - private static string PropertyTypeStatement(DeclarationType declarationType) - { - switch (declarationType) - { - case DeclarationType.PropertyGet: - return $"{Tokens.Property} {Tokens.Get}"; - case DeclarationType.PropertyLet: - return $"{Tokens.Property} {Tokens.Let}"; - case DeclarationType.PropertySet: - return $"{Tokens.Property} {Tokens.Set}"; - default: - throw new ArgumentException(); - } - - } } } diff --git a/Rubberduck.Refactorings/Common/ModuleBodyElementDeclarationExtensions.cs b/Rubberduck.Refactorings/Common/ModuleBodyElementDeclarationExtensions.cs deleted file mode 100644 index 5b2bd87671..0000000000 --- a/Rubberduck.Refactorings/Common/ModuleBodyElementDeclarationExtensions.cs +++ /dev/null @@ -1,176 +0,0 @@ -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Symbols; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Rubberduck.Refactorings.Common -{ - public static class ModuleBodyElementDeclarationExtensions - { - /// - /// Returns ModuleBodyElementDeclaration signature with an ImprovedArgument list - /// 1. Explicitly declares Property Let\Set value parameter as ByVal - /// 2. Ensures UserDefined Type parameters are declared either explicitly or implicitly as ByRef - /// - /// - /// - public static string FullMemberSignature(this ModuleBodyElementDeclaration declaration, - string accessibility = null, - string newIdentifier = null) - { - var accessibilityToken = declaration.Accessibility.Equals(Accessibility.Implicit) - ? Tokens.Public - : $"{declaration.Accessibility.ToString()}"; - - var asTypeClause = declaration.AsTypeName == null - ? string.Empty - : $" {Tokens.As} {declaration.AsTypeName}"; - - var fullSignatureFormat = RetrieveFullSignatureFormat(declaration); - - var fullSignature = string.Format(fullSignatureFormat, - accessibility ?? accessibilityToken, - newIdentifier ?? declaration.IdentifierName, - ImprovedArgumentList(declaration), - asTypeClause); - - return fullSignature.Trim(); - } - - public static string AsCodeBlock(this ModuleBodyElementDeclaration declaration, - string content = null, - string accessibility = null, - string newIdentifier = null) - { - var endStatement = string.Empty; - switch (declaration.Context) - { - case VBAParser.SubStmtContext _: - endStatement = $"{Tokens.End} {Tokens.Sub}"; - break; - case VBAParser.FunctionStmtContext _: - endStatement = $"{Tokens.End} {Tokens.Function}"; - break; - case VBAParser.PropertyGetStmtContext _: - case VBAParser.PropertyLetStmtContext _: - case VBAParser.PropertySetStmtContext _: - endStatement = $"{Tokens.End} {Tokens.Property}"; - break; - default: - throw new ArgumentException(); - } - - if (content != null) - { - return string.Format("{0}{1}{2}{1}{3}{1}", - FullMemberSignature(declaration, accessibility, newIdentifier), - Environment.NewLine, - content, - endStatement); - } - - return string.Format("{0}{1}{2}{1}", - FullMemberSignature(declaration, accessibility, newIdentifier), - Environment.NewLine, - endStatement); - } - - /// - /// 1. Explicitly declares Property Let\Set value parameter as ByVal - /// 2. Ensures UserDefined Type parameters are declared either explicitly or implicitly as ByRef - /// - /// - /// - public static string ImprovedArgumentList(this ModuleBodyElementDeclaration declaration) - { - var arguments = Enumerable.Empty(); - if (declaration is IParameterizedDeclaration parameterizedDeclaration) - { - arguments = parameterizedDeclaration.Parameters - .OrderBy(parameter => parameter.Selection) - .Select(parameter => BuildParameterDeclaration( - parameter, - parameter.Equals(parameterizedDeclaration.Parameters.LastOrDefault()) - && declaration.DeclarationType.HasFlag(DeclarationType.Property) - && !declaration.DeclarationType.Equals(DeclarationType.PropertyGet))); - } - return $"{string.Join(", ", arguments)}"; - } - - private static string BuildParameterDeclaration(ParameterDeclaration parameter, bool forceExplicitByValAccess) - { - var accessibility = parameter.IsImplicitByRef - ? string.Empty - : parameter.IsByRef - ? Tokens.ByRef - : Tokens.ByVal; - - if (forceExplicitByValAccess) - { - accessibility = Tokens.ByVal; - } - - if (accessibility.Equals(Tokens.ByVal) - && (parameter.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false)) - { - accessibility = Tokens.ByRef; - } - - var name = parameter.IsArray - ? $"{parameter.IdentifierName}()" - : parameter.IdentifierName; - - var optional = parameter.IsParamArray - ? Tokens.ParamArray - : parameter.IsOptional - ? Tokens.Optional - : string.Empty; - - var defaultValue = parameter.DefaultValue; - - return $"{FormatStandardElement(optional)}{FormatStandardElement(accessibility)}{FormatStandardElement(name)}{FormattedAsTypeName(parameter.AsTypeName)}{FormattedDefaultValue(defaultValue)}".Trim(); - } - - private static string RetrieveFullSignatureFormat(Declaration declaration) - { - var fullSignatureFormat = $"{{0}} THE_MEMBER_TYPE {{1}}({{2}}){{3}}"; - - switch (declaration.Context) - { - case VBAParser.SubStmtContext _: - fullSignatureFormat = fullSignatureFormat.Replace("THE_MEMBER_TYPE", Tokens.Sub); - break; - case VBAParser.FunctionStmtContext _: - fullSignatureFormat = fullSignatureFormat.Replace("THE_MEMBER_TYPE", Tokens.Function); - break; - case VBAParser.PropertyGetStmtContext _: - fullSignatureFormat = fullSignatureFormat.Replace("THE_MEMBER_TYPE", $"{Tokens.Property} {Tokens.Get}"); - break; - case VBAParser.PropertyLetStmtContext _: - fullSignatureFormat = fullSignatureFormat.Replace("THE_MEMBER_TYPE", $"{Tokens.Property} {Tokens.Let}"); - break; - case VBAParser.PropertySetStmtContext _: - fullSignatureFormat = fullSignatureFormat.Replace("THE_MEMBER_TYPE", $"{Tokens.Property} {Tokens.Set}"); - break; - default: - throw new ArgumentException(); - } - return fullSignatureFormat; - } - - private static string FormatStandardElement(string element) => string.IsNullOrEmpty(element) - ? string.Empty - : $"{element} "; - - private static string FormattedAsTypeName(string AsTypeName) => string.IsNullOrEmpty(AsTypeName) - ? string.Empty - : $"As {AsTypeName} "; - - private static string FormattedDefaultValue(string DefaultValue) => string.IsNullOrEmpty(DefaultValue) - ? string.Empty - : $"= {DefaultValue}"; - } -} \ No newline at end of file diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs index 8e8efbe774..9626ef1eb5 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs @@ -27,6 +27,7 @@ public class EncapsulateFieldRefactoring : InteractiveRefactoringBase f.PropertyAttributeSets); - - foreach (var selectedField in SelectedFields) - { - var converted = selectedField as IConvertToUDTMember; - foreach (var set in selectedField.PropertyAttributeSets) - { - if (converted.Declaration is VariableDeclaration variableDeclaration) - { - var getContent = $"{set.PropertyName} = {set.BackingField}"; - if (set.UsesSetAssignment) - { - getContent = $"{Tokens.Set} {getContent}"; - } - AddContentBlock(NewContentTypes.MethodBlock, variableDeclaration.FieldToPropertyBlock(DeclarationType.PropertyGet, set.PropertyName, content: $"{_defaultIndent}{getContent}")); - if (converted.IsReadOnly) - { - continue; - } - if (set.GenerateLetter) - { - AddContentBlock(NewContentTypes.MethodBlock, variableDeclaration.FieldToPropertyBlock(DeclarationType.PropertyLet, set.PropertyName, content: $"{_defaultIndent}{set.BackingField} = {set.ParameterName}")); - } - if (set.GenerateSetter) - { - AddContentBlock(NewContentTypes.MethodBlock, variableDeclaration.FieldToPropertyBlock(DeclarationType.PropertySet, set.PropertyName, content: $"{_defaultIndent}{Tokens.Set} {set.BackingField} = {set.ParameterName}")); - } - } - } - } - } - protected override void LoadFieldReferenceContextReplacements(IEncapsulateFieldCandidate field) { Debug.Assert(field is IConvertToUDTMember); diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/EncapsulateFieldStrategyBase.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/EncapsulateFieldStrategyBase.cs index c84a667889..bf6c9f2d12 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/EncapsulateFieldStrategyBase.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/EncapsulateFieldStrategyBase.cs @@ -10,6 +10,7 @@ using Rubberduck.VBEditor; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -41,6 +42,7 @@ public abstract class EncapsulateFieldStrategyBase : IEncapsulateStrategy protected QualifiedModuleName _targetQMN; private readonly int? _codeSectionStartIndex; protected const string _defaultIndent = " "; //4 spaces + protected ICodeBuilder _codeBuilder; protected Dictionary IdentifierReplacements { get; } = new Dictionary(); @@ -50,10 +52,11 @@ protected enum NewContentTypes { TypeDeclarationBlock, DeclarationBlock, MethodB protected IEnumerable SelectedFields { private set; get; } - public EncapsulateFieldStrategyBase(IDeclarationFinderProvider declarationFinderProvider, EncapsulateFieldModel model, IIndenter indenter) + public EncapsulateFieldStrategyBase(IDeclarationFinderProvider declarationFinderProvider, EncapsulateFieldModel model, IIndenter indenter, ICodeBuilder codeBuilder) { _targetQMN = model.QualifiedModuleName; _indenter = indenter; + _codeBuilder = codeBuilder; SelectedFields = model.SelectedFieldCandidates; _codeSectionStartIndex = declarationFinderProvider.DeclarationFinder @@ -138,39 +141,62 @@ private void InsertNewContent(IEncapsulateFieldRewriteSession refactorRewriteSes } } - protected virtual void LoadNewPropertyBlocks() + protected void LoadNewPropertyBlocks() { - foreach (var attributeSet in SelectedFields.SelectMany(f => f.PropertyAttributeSets)) + foreach (var propertyAttributes in SelectedFields.SelectMany(f => f.PropertyAttributeSets)) { - if (attributeSet.Declaration is VariableDeclaration || attributeSet.Declaration.DeclarationType.Equals(DeclarationType.UserDefinedTypeMember)) - { - var getContent = $"{attributeSet.PropertyName} = {attributeSet.BackingField}"; - if (attributeSet.UsesSetAssignment) - { - getContent = $"{Tokens.Set} {getContent}"; - } - if (attributeSet.AsTypeName.Equals(Tokens.Variant) && !attributeSet.Declaration.IsArray) - { - getContent = string.Join(Environment.NewLine, - $"{Tokens.If} IsObject({attributeSet.BackingField}) {Tokens.Then}", - $"{_defaultIndent}{Tokens.Set} {attributeSet.PropertyName} = {attributeSet.BackingField}", - Tokens.Else, - $"{_defaultIndent}{attributeSet.PropertyName} = {attributeSet.BackingField}", - $"{Tokens.End} {Tokens.If}", - Environment.NewLine); - } + AddPropertyCodeBlocks(propertyAttributes); + } + } + + private void AddPropertyCodeBlocks(PropertyAttributeSet propertyAttributes) + { + Debug.Assert(propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.Variable) || propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember)); - AddContentBlock(NewContentTypes.MethodBlock, attributeSet.Declaration.FieldToPropertyBlock(DeclarationType.PropertyGet, attributeSet.PropertyName, content: $"{_defaultIndent}{getContent}")); + var getContent = $"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}"; + if (propertyAttributes.UsesSetAssignment) + { + getContent = $"{Tokens.Set} {getContent}"; + } - if (attributeSet.GenerateLetter) - { - AddContentBlock(NewContentTypes.MethodBlock, attributeSet.Declaration.FieldToPropertyBlock(DeclarationType.PropertyLet, attributeSet.PropertyName, content: $"{_defaultIndent}{attributeSet.BackingField} = {attributeSet.ParameterName}")); - } - if (attributeSet.GenerateSetter) - { - AddContentBlock(NewContentTypes.MethodBlock, attributeSet.Declaration.FieldToPropertyBlock(DeclarationType.PropertySet, attributeSet.PropertyName, content: $"{_defaultIndent}{Tokens.Set} {attributeSet.BackingField} = {attributeSet.ParameterName}")); - } + if (propertyAttributes.AsTypeName.Equals(Tokens.Variant) && !propertyAttributes.Declaration.IsArray) + { + getContent = string.Join(Environment.NewLine, + $"{Tokens.If} IsObject({propertyAttributes.BackingField}) {Tokens.Then}", + $"{_defaultIndent}{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", + Tokens.Else, + $"{_defaultIndent}{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", + $"{Tokens.End} {Tokens.If}", + Environment.NewLine); + } + + if (!_codeBuilder.TryBuildPropertyGetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyGet, content: $"{_defaultIndent}{getContent}")) + { + throw new ArgumentException(); + } + AddContentBlock(NewContentTypes.MethodBlock, propertyGet); + + if (!(propertyAttributes.GenerateLetter || propertyAttributes.GenerateSetter)) + { + return; + } + + if (propertyAttributes.GenerateLetter) + { + if (!_codeBuilder.TryBuildPropertyLetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyLet, content: $"{_defaultIndent}{propertyAttributes.BackingField} = {propertyAttributes.ParameterName}")) + { + throw new ArgumentException(); + } + AddContentBlock(NewContentTypes.MethodBlock, propertyLet); + } + + if (propertyAttributes.GenerateSetter) + { + if (!_codeBuilder.TryBuildPropertySetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertySet, content: $"{_defaultIndent}{Tokens.Set} {propertyAttributes.BackingField} = {propertyAttributes.ParameterName}")) + { + throw new ArgumentException(); } + AddContentBlock(NewContentTypes.MethodBlock, propertySet); } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/UseBackingFields.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/UseBackingFields.cs index 7172200a33..b4f5b16dfb 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/UseBackingFields.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/UseBackingFields.cs @@ -16,8 +16,8 @@ namespace Rubberduck.Refactorings.EncapsulateField { public class UseBackingFields : EncapsulateFieldStrategyBase { - public UseBackingFields(IDeclarationFinderProvider declarationFinderProvider, EncapsulateFieldModel model, IIndenter indenter) - : base(declarationFinderProvider, model, indenter){ } + public UseBackingFields(IDeclarationFinderProvider declarationFinderProvider, EncapsulateFieldModel model, IIndenter indenter, ICodeBuilder codeBuilder) + : base(declarationFinderProvider, model, indenter, codeBuilder) { } protected override void ModifyFields(IEncapsulateFieldRewriteSession refactorRewriteSession) { diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs index 22af322d07..ec40082048 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceModel.cs @@ -34,7 +34,7 @@ public class ExtractInterfaceModel : IRefactoringModel DeclarationType.PropertySet, }; - public ExtractInterfaceModel(IDeclarationFinderProvider declarationFinderProvider, ClassModuleDeclaration target) + public ExtractInterfaceModel(IDeclarationFinderProvider declarationFinderProvider, ClassModuleDeclaration target, ICodeBuilder codeBuilder) { TargetDeclaration = target; DeclarationFinderProvider = declarationFinderProvider; @@ -47,10 +47,10 @@ public ExtractInterfaceModel(IDeclarationFinderProvider declarationFinderProvide InterfaceName = $"I{TargetDeclaration.IdentifierName}"; InterfaceInstancing = ImplementingClassInstancing; - LoadMembers(); + LoadMembers(codeBuilder); } - private void LoadMembers() + private void LoadMembers(ICodeBuilder codeBuilder) { Members = new ObservableCollection(DeclarationFinderProvider.DeclarationFinder .Members(TargetDeclaration.QualifiedModuleName) @@ -59,7 +59,7 @@ private void LoadMembers() && MemberTypes.Contains(item.DeclarationType)) .OrderBy(o => o.Selection.StartLine) .ThenBy(t => t.Selection.StartColumn) - .Select(d => new InterfaceMember(d)) + .Select(d => new InterfaceMember(d, codeBuilder)) .ToList()); } } diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs index baaa7073db..23390caa1e 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs @@ -14,17 +14,20 @@ public class ExtractInterfaceRefactoring : InteractiveRefactoringBase _refactoringAction; private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly ICodeBuilder _codeBuilder; public ExtractInterfaceRefactoring( ExtractInterfaceRefactoringAction refactoringAction, IDeclarationFinderProvider declarationFinderProvider, IRefactoringPresenterFactory factory, ISelectionProvider selectionProvider, - IUiDispatcher uiDispatcher) + IUiDispatcher uiDispatcher, + ICodeBuilder codeBuilder) :base(selectionProvider, factory, uiDispatcher) { _refactoringAction = refactoringAction; _declarationFinderProvider = declarationFinderProvider; + _codeBuilder = codeBuilder; } private static readonly DeclarationType[] ModuleTypes = @@ -57,7 +60,7 @@ protected override ExtractInterfaceModel InitializeModel(Declaration target) throw new InvalidDeclarationTypeException(target); } - return new ExtractInterfaceModel(_declarationFinderProvider, targetClass); + return new ExtractInterfaceModel(_declarationFinderProvider, targetClass, _codeBuilder); } protected override void RefactorImpl(ExtractInterfaceModel model) diff --git a/Rubberduck.Refactorings/ExtractInterface/InterfaceMember.cs b/Rubberduck.Refactorings/ExtractInterface/InterfaceMember.cs index 7cfaf3ed05..e8b77e74f8 100644 --- a/Rubberduck.Refactorings/ExtractInterface/InterfaceMember.cs +++ b/Rubberduck.Refactorings/ExtractInterface/InterfaceMember.cs @@ -1,20 +1,16 @@ using System; -using System.Collections.Generic; using System.ComponentModel; -using System.Linq; using System.Runtime.CompilerServices; -using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Symbols; -using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.ImplementInterface; namespace Rubberduck.Refactorings.ExtractInterface { public class InterfaceMember : INotifyPropertyChanged { private readonly ModuleBodyElementDeclaration _element; + private readonly ICodeBuilder _codeBuilder; - public InterfaceMember(Declaration member) + public InterfaceMember(Declaration member, ICodeBuilder codeBuilder) { Member = member; if (!(member is ModuleBodyElementDeclaration mbed)) @@ -22,6 +18,7 @@ public InterfaceMember(Declaration member) throw new ArgumentException(); } _element = mbed; + _codeBuilder = codeBuilder; } public Declaration Member { get; } @@ -37,9 +34,9 @@ public bool IsSelected } } - public string FullMemberSignature => _element.FullMemberSignature(); + public string FullMemberSignature => _codeBuilder.ImprovedFullMemberSignature(_element); - public string Body => _element.AsCodeBlock(); + public string Body => _codeBuilder.BuildMemberBlockFromPrototype(_element); public event PropertyChangedEventHandler PropertyChanged; diff --git a/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs b/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs index 67c66b982e..f56913b69b 100644 --- a/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs +++ b/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs @@ -4,19 +4,19 @@ using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; -using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.ImplementInterface; namespace Rubberduck.Refactorings.AddInterfaceImplementations { public class AddInterfaceImplementationsRefactoringAction : CodeOnlyRefactoringActionBase { private readonly string _memberBody; + private readonly ICodeBuilder _codeBuilder; - public AddInterfaceImplementationsRefactoringAction(IRewritingManager rewritingManager) + public AddInterfaceImplementationsRefactoringAction(IRewritingManager rewritingManager, ICodeBuilder codeBuilder) : base(rewritingManager) { _memberBody = $" {Tokens.Err}.Raise 5 {Resources.RubberduckUI.ImplementInterface_TODO}"; + _codeBuilder = codeBuilder; } public override void Refactor(AddInterfaceImplementationsModel model, IRewriteSession rewriteSession) @@ -37,23 +37,33 @@ private string GetInterfaceMember(Declaration member, string interfaceName) { if (member is ModuleBodyElementDeclaration mbed) { - return mbed.AsCodeBlock(accessibility: Tokens.Private, newIdentifier: $"{interfaceName}_{member.IdentifierName}", content: _memberBody); + return _codeBuilder.BuildMemberBlockFromPrototype(mbed, accessibility: Tokens.Private, newIdentifier: $"{interfaceName}_{member.IdentifierName}", content: _memberBody); } - if (member.DeclarationType.Equals(DeclarationType.Variable)) + if (member is VariableDeclaration variable) { - var propertyGet = member.FieldToPropertyBlock(DeclarationType.PropertyGet, $"{interfaceName}_{member.IdentifierName}", Tokens.Private, _memberBody); + if (!_codeBuilder.TryBuildPropertyGetCodeBlock(variable, $"{interfaceName}_{variable.IdentifierName}", out var propertyGet, Tokens.Private, _memberBody)) + { + throw new InvalidOperationException(); + } + var members = new List { propertyGet }; - if (member.AsTypeName.Equals(Tokens.Variant) || !member.IsObject) + if (variable.AsTypeName.Equals(Tokens.Variant) || !variable.IsObject) { - var propertyLet = member.FieldToPropertyBlock(DeclarationType.PropertyLet, $"{interfaceName}_{member.IdentifierName}", Tokens.Private, _memberBody); + if (!_codeBuilder.TryBuildPropertyLetCodeBlock(variable, $"{interfaceName}_{variable.IdentifierName}", out var propertyLet, Tokens.Private, _memberBody)) + { + throw new InvalidOperationException(); + } members.Add(propertyLet); } - if (member.AsTypeName.Equals(Tokens.Variant) || member.IsObject) + if (variable.AsTypeName.Equals(Tokens.Variant) || variable.IsObject) { - var propertySet = member.FieldToPropertyBlock(DeclarationType.PropertySet, $"{interfaceName}_{member.IdentifierName}", Tokens.Private, _memberBody); + if (!_codeBuilder.TryBuildPropertySetCodeBlock(variable, $"{interfaceName}_{variable.IdentifierName}", out var propertySet, Tokens.Private, _memberBody)) + { + throw new InvalidOperationException(); + } members.Add(propertySet); } diff --git a/RubberduckTests/CodeBuilderTests.cs b/RubberduckTests/CodeBuilderTests.cs new file mode 100644 index 0000000000..58326a60be --- /dev/null +++ b/RubberduckTests/CodeBuilderTests.cs @@ -0,0 +1,413 @@ +using NUnit.Framework; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Refactorings; +using RubberduckTests.Mocks; +using System; +using System.Linq; + +namespace RubberduckTests +{ + [TestFixture] + public class CodeBuilderTests + { + + [TestCase("fizz", DeclarationType.Variable, "Integer")] + [TestCase("FirstValue", DeclarationType.UserDefinedTypeMember, "Long")] + [TestCase("fazz", DeclarationType.Variable, "Long")] + [TestCase("fuzz", DeclarationType.Variable, "ETestType2")] + [Category(nameof(CodeBuilder))] + public void PropertyBlockFromPrototype_PropertyGet(string targetIdentifier, DeclarationType declarationType, string typeName) + { + var testParams = new PropertyBlockFromPrototypeParams("Bazz", DeclarationType.PropertyGet); + string inputCode = +$@" + +Private Type TTestType + FirstValue As Long + SecondValue As Variant +End Type + +Private Enum ETestType + EFirstValue = 0 + ESecondValue +End Enum + +Public Enum ETestType2 + EThirdValue = 0 + EFourthValue +End Enum + +Private fizz As Integer + +Private fazz As ETestType + +Private fuzz As ETestType2 +"; + var result = ParseAndTest(inputCode, + targetIdentifier, + declarationType, + testParams, + PropertyGetBlockFromPrototypeTest); + + StringAssert.Contains($"Property Get {testParams.Identifier}() As {typeName}", result); + } + + [TestCase("fizz", DeclarationType.Variable, "Integer", "Public")] + [TestCase("FirstValue", DeclarationType.UserDefinedTypeMember, "Long", "Public")] + [TestCase("fazz", DeclarationType.Variable, "Long", "Public")] + [TestCase("fuzz", DeclarationType.Variable, "ETestType2", "Private")] + [Category(nameof(CodeBuilder))] + public void PropertyBlockFromPrototype_PropertyGetAccessibility(string targetIdentifier, DeclarationType declarationType, string typeName, string accessibility) + { + var testParams = new PropertyBlockFromPrototypeParams("Bazz", DeclarationType.PropertyGet, accessibility); + string inputCode = +$@" + +Private Type TTestType + FirstValue As Long + SecondValue As Variant +End Type + +Private Enum ETestType + EFirstValue = 0 + ESecondValue +End Enum + +Public Enum ETestType2 + EThirdValue = 0 + EFourthValue +End Enum + +Private fizz As Integer + +Private fazz As ETestType + +Private fuzz As ETestType2 +"; + var result = ParseAndTest(inputCode, + targetIdentifier, + declarationType, + testParams, + PropertyGetBlockFromPrototypeTest); + + StringAssert.Contains($"{accessibility} Property Get {testParams.Identifier}() As {typeName}", result); + } + + [TestCase("fizz", DeclarationType.Variable, "Integer", "Bazz = fizz")] + [TestCase("FirstValue", DeclarationType.UserDefinedTypeMember, "Long", "Bazz = fozz.FirstValue")] + [TestCase("fazz", DeclarationType.Variable, "Long", "Bazz = fazz")] + [TestCase("fuzz", DeclarationType.Variable, "TTestType2", "Bazz = fuzz")] + [Category(nameof(CodeBuilder))] + public void PropertyBlockFromPrototype_PropertyGetContent(string targetIdentifier, DeclarationType declarationType, string typeName, string content) + { + var testParams = new PropertyBlockFromPrototypeParams("Bazz", DeclarationType.PropertyGet, content: content); + string inputCode = +$@" + +Private Type TTestType + FirstValue As Long + SecondValue As Variant +End Type + +Private Enum ETestType + EFirstValue = 0 + ESecondValue +End Enum + +Public Enum ETestType2 + EThirdValue = 0 + EFourthValue +End Enum + +Private fizz As Integer + +Private fozz As TTestType + +Private fazz As ETestType + +Private fuzz As TTestType2 +"; + var result = ParseAndTest(inputCode, + targetIdentifier, + declarationType, + testParams, + PropertyGetBlockFromPrototypeTest); + + StringAssert.Contains(content, result); + } + + + [TestCase("fizz", DeclarationType.Variable, "Integer", "Bazz = fizz")] + [Category(nameof(CodeBuilder))] + public void PropertyBlockFromPrototype_PropertyGetChangeParamName(string targetIdentifier, DeclarationType declarationType, string typeName, string content) + { + var testParams = new PropertyBlockFromPrototypeParams("Bazz", DeclarationType.PropertyGet, paramIdentifier: "testParam"); + string inputCode = +$@" +Private fizz As Integer +"; + var result = ParseAndTest(inputCode, + targetIdentifier, + declarationType, + testParams, + PropertyGetBlockFromPrototypeTest); + + StringAssert.Contains("Property Get Bazz() As Integer", result); + } + + [TestCase("fizz", DeclarationType.Variable, "Integer")] + [TestCase("FirstValue", DeclarationType.UserDefinedTypeMember, "Long")] + [TestCase("fazz", DeclarationType.Variable, "Long")] + [TestCase("fuzz", DeclarationType.Variable, "ETestType2")] + [Category(nameof(CodeBuilder))] + public void PropertyBlockFromPrototype_PropertyLet(string targetIdentifier, DeclarationType declarationType, string typeName) + { + var testParams = new PropertyBlockFromPrototypeParams("Bazz", DeclarationType.PropertyLet); + string inputCode = +$@" + +Private Type TTestType + FirstValue As Long + SecondValue As Variant +End Type + +Private Enum ETestType + EFirstValue = 0 + ESecondValue +End Enum + +Public Enum ETestType2 + EThirdValue = 0 + EFourthValue +End Enum + +Private fizz As Integer + +Private fazz As ETestType + +Private fuzz As ETestType2 +"; + var result = ParseAndTest(inputCode, + targetIdentifier, + declarationType, + testParams, + PropertyLetBlockFromPrototypeTest); + + StringAssert.Contains($"Property Let {testParams.Identifier}(ByVal value As {typeName})", result); + } + + [TestCase("fizz", DeclarationType.Variable, "Variant")] + [TestCase("SecondValue", DeclarationType.UserDefinedTypeMember, "Variant")] + [Category(nameof(CodeBuilder))] + public void PropertyBlockFromPrototype_PropertySet(string targetIdentifier, DeclarationType declarationType, string typeName) + { + var testParams = new PropertyBlockFromPrototypeParams("Bazz", DeclarationType.PropertySet); + string inputCode = +$@" + +Private Type TTestType + FirstValue As Long + SecondValue As Variant +End Type + +Private fizz As Variant + +"; + var result = ParseAndTest(inputCode, + targetIdentifier, + declarationType, + testParams, + PropertySetBlockFromPrototypeTest); + + StringAssert.Contains($"Property Set {testParams.Identifier}(ByVal value As {typeName})", result); + } + + [TestCase(DeclarationType.PropertyLet)] + [TestCase(DeclarationType.PropertySet)] + [TestCase(DeclarationType.Procedure)] + [Category(nameof(CodeBuilder))] + public void MemberBlockFromPrototype_AppliesByVal(DeclarationType declarationType) + { + var procedureIdentifier = "TestProcedure"; + var procType = ProcedureTypeIdentifier(declarationType); + + string inputCode = +$@" +Public {procType.procType} {procedureIdentifier}(arg1 As Long, arg2 As String) +End {procType.endStmt} +"; + var result = ParseAndTest(inputCode, + procedureIdentifier, + declarationType, + new MemberBlockFromPrototypeTestParams(), + MemberBlockFromPrototypeTest); + + var expected = declarationType.HasFlag(DeclarationType.Property) + ? "(arg1 As Long, ByVal arg2 As String)" + : "(arg1 As Long, arg2 As String)"; + + StringAssert.Contains($"{procType.procType} {procedureIdentifier}{expected}", result); + } + + [TestCase(DeclarationType.PropertyLet)] + [TestCase(DeclarationType.PropertySet)] + [TestCase(DeclarationType.Procedure)] + [Category(nameof(CodeBuilder))] + public void ImprovedArgumentList_AppliesByVal(DeclarationType declarationType) + { + var procedureIdentifier = "TestProperty"; + var procType = ProcedureTypeIdentifier(declarationType); + + string inputCode = +$@" +Public {procType.procType} {procedureIdentifier}(arg1 As Long, arg2 As String) +End {procType.endStmt} +"; + var result = ParseAndTest(inputCode, + procedureIdentifier, + declarationType, + ImprovedArgumentListTest); + + var expected = declarationType.HasFlag(DeclarationType.Property) + ? "arg1 As Long, ByVal arg2 As String" + : "arg1 As Long, arg2 As String"; + + StringAssert.AreEqualIgnoringCase(expected, result); + } + + + [TestCase(DeclarationType.PropertyGet)] + [TestCase(DeclarationType.Function)] + [Category(nameof(CodeBuilder))] + public void ImprovedArgumentList_FunctionTypes(DeclarationType declarationType) + { + var procedureIdentifier = "TestProperty"; + var procType = ProcedureTypeIdentifier(declarationType); + + string inputCode = +$@" +Public {procType.procType} {procedureIdentifier}(arg1 As Long, arg2 As String) As Long +End {procType.endStmt} +"; + var result = ParseAndTest(inputCode, + procedureIdentifier, + declarationType, + ImprovedArgumentListTest); + + StringAssert.AreEqualIgnoringCase($"arg1 As Long, arg2 As String", result); + } + + private string ParseAndTest(string inputCode, string targetIdentifier, DeclarationType declarationType, Func theTest) + { + var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; + var state = MockParser.CreateAndParse(vbe); + using (state) + { + var target = state.DeclarationFinder.DeclarationsWithType(declarationType) + .Where(d => d.IdentifierName == targetIdentifier).OfType() + .Single(); + return theTest(target); + } + } + + private string ParseAndTest(string inputCode, string targetIdentifier, DeclarationType declarationType, MemberBlockFromPrototypeTestParams testParams, Func theTest) + { + var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; + var state = MockParser.CreateAndParse(vbe); + using (state) + { + var target = state.DeclarationFinder.DeclarationsWithType(declarationType) + .Where(d => d.IdentifierName == targetIdentifier).OfType() + .Single(); + return theTest(target, testParams); + } + } + + private string ParseAndTest(string inputCode, string targetIdentifier, DeclarationType declarationType, PropertyBlockFromPrototypeParams testParams, Func theTest) + { + var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; + var state = MockParser.CreateAndParse(vbe); + using (state) + { + var target = state.DeclarationFinder.DeclarationsWithType(declarationType) + .Where(d => d.IdentifierName == targetIdentifier).OfType() + .Single(); + return theTest(target, testParams); + } + } + + private static string PropertyGetBlockFromPrototypeTest(T target, PropertyBlockFromPrototypeParams testParams) where T : Declaration + { + new CodeBuilder().TryBuildPropertyGetCodeBlock(target, testParams.Identifier, out string result, testParams.Accessibility, testParams.Content); //, testParams.WriteParam); + return result; + } + + private static string PropertyLetBlockFromPrototypeTest(T target, PropertyBlockFromPrototypeParams testParams) where T : Declaration + { + new CodeBuilder().TryBuildPropertyLetCodeBlock(target, testParams.Identifier, out string result, testParams.Accessibility, testParams.Content, testParams.WriteParam); + return result; + } + + private static string PropertySetBlockFromPrototypeTest(T target, PropertyBlockFromPrototypeParams testParams) where T : Declaration + { + new CodeBuilder().TryBuildPropertySetCodeBlock(target, testParams.Identifier, out string result, testParams.Accessibility, testParams.Content, testParams.WriteParam); + return result; + } + + private static string ImprovedArgumentListTest(ModuleBodyElementDeclaration mbed) + => new CodeBuilder().ImprovedArgumentList(mbed); + + private static string MemberBlockFromPrototypeTest(ModuleBodyElementDeclaration mbed, MemberBlockFromPrototypeTestParams testParams) + => new CodeBuilder().BuildMemberBlockFromPrototype(mbed, testParams.Accessibility, testParams.Content, testParams.NewIdentifier); + + private (string procType, string endStmt) ProcedureTypeIdentifier(DeclarationType declarationType) + { + switch (declarationType) + { + case DeclarationType.Function: + return ("Function", "Function"); + case DeclarationType.Procedure: + return ("Sub", "Sub"); + case DeclarationType.PropertyGet: + return ("Property Get", "Property"); + case DeclarationType.PropertyLet: + return ("Property Let", "Property"); + case DeclarationType.PropertySet: + return ("Property Set", "Property"); + default: + throw new ArgumentOutOfRangeException(); + } + } + + private struct PropertyBlockFromPrototypeParams + { + public PropertyBlockFromPrototypeParams(string identifier, DeclarationType propertyType, string accessibility = null, string content = null, string paramIdentifier = null) + { + Identifier = identifier; + DeclarationType = propertyType; + Accessibility = accessibility; + Content = content; + WriteParam = paramIdentifier; + } + public DeclarationType DeclarationType { get; } + public string Identifier { get; } + public string Accessibility {get; } + public string Content { get; } + public string WriteParam { get; } + } + + private struct MemberBlockFromPrototypeTestParams + { + public MemberBlockFromPrototypeTestParams(ModuleBodyElementDeclaration mbed, string accessibility = null, string content = null, string newIdentifier = null) + { + Accessibility = accessibility; + Content = content; + NewIdentifier = newIdentifier; + } + + public string Accessibility { get; } + public string Content { get; } + public string NewIdentifier { get; } + } + } +} diff --git a/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs b/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs index 923d99e59a..5923b83b50 100644 --- a/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs +++ b/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs @@ -32,6 +32,7 @@ using Rubberduck.VBEditor.SourceCodeHandling; using Rubberduck.VBEditor.Utility; using RubberduckTests.Settings; +using Rubberduck.Refactorings; namespace RubberduckTests.CodeExplorer { @@ -503,11 +504,11 @@ public MockedCodeExplorer ImplementIndenterCommand() public MockedCodeExplorer ImplementExtractInterfaceCommand() { - var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(null); + var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(null, new CodeBuilder()); var addComponentService = TestAddComponentService(State.ProjectsProvider); var extractInterfaceBaseRefactoring = new ExtractInterfaceRefactoringAction(addImplementationsBaseRefactoring, State, State, null, State.ProjectsProvider, addComponentService); ViewModel.CodeExplorerExtractInterfaceCommand = new CodeExplorerExtractInterfaceCommand( - new ExtractInterfaceRefactoring(extractInterfaceBaseRefactoring, State, null, null, _uiDispatcher.Object), + new ExtractInterfaceRefactoring(extractInterfaceBaseRefactoring, State, null, null, _uiDispatcher.Object, new CodeBuilder()), State, null, VbeEvents.Object); return this; } diff --git a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs index be99bd1c97..1ba718db45 100644 --- a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs +++ b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs @@ -63,7 +63,7 @@ protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state uiDispatcherMock .Setup(m => m.Invoke(It.IsAny())) .Callback((Action action) => action.Invoke()); - var refactoring = new EncapsulateFieldRefactoring(state, null, factory, rewritingManager, selectionService, selectedDeclarationProvider, uiDispatcherMock.Object); + var refactoring = new EncapsulateFieldRefactoring(state, null, factory, rewritingManager, selectionService, selectedDeclarationProvider, uiDispatcherMock.Object, new CodeBuilder()); var notifier = new EncapsulateFieldFailedNotifier(msgBox); var selectedDeclarationService = new SelectedDeclarationProvider(selectionService, state); return new RefactorEncapsulateFieldCommand(refactoring, notifier, state, selectionService, selectedDeclarationService); diff --git a/RubberduckTests/Commands/RefactorCommands/ExtractInterfaceCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/ExtractInterfaceCommandTests.cs index e81a8ddde8..71aedda6e7 100644 --- a/RubberduckTests/Commands/RefactorCommands/ExtractInterfaceCommandTests.cs +++ b/RubberduckTests/Commands/RefactorCommands/ExtractInterfaceCommandTests.cs @@ -173,10 +173,10 @@ protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state uiDispatcherMock .Setup(m => m.Invoke(It.IsAny())) .Callback((Action action) => action.Invoke()); - var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager); + var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); var addComponentService = TestAddComponentService(state.ProjectsProvider); var baseRefactoring = new ExtractInterfaceRefactoringAction(addImplementationsBaseRefactoring, state, state, rewritingManager, state.ProjectsProvider, addComponentService); - var refactoring = new ExtractInterfaceRefactoring(baseRefactoring, state, factory, selectionService, uiDispatcherMock.Object); + var refactoring = new ExtractInterfaceRefactoring(baseRefactoring, state, factory, selectionService, uiDispatcherMock.Object, new CodeBuilder()); var notifier = new ExtractInterfaceFailedNotifier(msgBox); return new RefactorExtractInterfaceCommand(refactoring, notifier, state, selectionService); } diff --git a/RubberduckTests/Commands/RefactorCommands/ImplementInterfaceCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/ImplementInterfaceCommandTests.cs index a37e3cea0f..aa5d7590f7 100644 --- a/RubberduckTests/Commands/RefactorCommands/ImplementInterfaceCommandTests.cs +++ b/RubberduckTests/Commands/RefactorCommands/ImplementInterfaceCommandTests.cs @@ -3,6 +3,7 @@ using Rubberduck.Interaction; using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; using Rubberduck.Refactorings.AddInterfaceImplementations; using Rubberduck.Refactorings.ImplementInterface; using Rubberduck.UI.Command; @@ -57,7 +58,7 @@ public void ImplementInterface_CanExecute_ImplementsInterfaceSelected() protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state, IRewritingManager rewritingManager, ISelectionService selectionService) { var msgBox = new Mock().Object; - var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager); + var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); var baseRefactoring = new ImplementInterfaceRefactoringAction(addImplementationsBaseRefactoring, rewritingManager); var refactoring = new ImplementInterfaceRefactoring(baseRefactoring, state, selectionService); var notifier = new ImplementInterfaceFailedNotifier(msgBox); diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs index 6e91c24114..bee2ad870a 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs @@ -121,7 +121,7 @@ public IRefactoring SupportTestRefactoring(IRewritingManager rewritingManager, R uiDispatcherMock .Setup(m => m.Invoke(It.IsAny())) .Callback((Action action) => action.Invoke()); - return new EncapsulateFieldRefactoring(state, indenter, factory, rewritingManager, selectionService, selectedDeclarationProvider, uiDispatcherMock.Object); + return new EncapsulateFieldRefactoring(state, indenter, factory, rewritingManager, selectionService, selectedDeclarationProvider, uiDispatcherMock.Object, new CodeBuilder()); } public IEncapsulateFieldCandidate RetrieveEncapsulateFieldCandidate(string inputCode, string fieldName) diff --git a/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceRefactoringActionTests.cs b/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceRefactoringActionTests.cs index 5a8785ef51..349300abc7 100644 --- a/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceRefactoringActionTests.cs @@ -755,13 +755,13 @@ private static ExtractInterfaceModel TestModel(IDeclarationFinderProvider state, var targetClass = finder.UserDeclarations(DeclarationType.ClassModule) .OfType() .Single(module => module.IdentifierName == "Class"); - var model = new ExtractInterfaceModel(state, targetClass); + var model = new ExtractInterfaceModel(state, targetClass, new CodeBuilder()); return modelAdjustment(model); } protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) { - var addInterfaceImplementationsAction = new AddInterfaceImplementationsRefactoringAction(rewritingManager); + var addInterfaceImplementationsAction = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); var addComponentService = TestAddComponentService(state?.ProjectsProvider); return new ExtractInterfaceRefactoringAction(addInterfaceImplementationsAction, state, state, rewritingManager, state?.ProjectsProvider, addComponentService); } diff --git a/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceTests.cs b/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceTests.cs index cf9c7b90d2..e2a6bda396 100644 --- a/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceTests.cs +++ b/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceTests.cs @@ -150,7 +150,7 @@ public void ExtractInterfaceRefactoring_IgnoresField() .First(); //Specify Params to remove - var model = new ExtractInterfaceModel(state, target); + var model = new ExtractInterfaceModel(state, target, new CodeBuilder()); Assert.AreEqual(0, model.Members.Count); } } @@ -178,7 +178,7 @@ public void ExtractInterfaceRefactoring_DefaultsToPublicInterfaceForExposedImple .First(); //Specify Params to remove - var model = new ExtractInterfaceModel(state, target); + var model = new ExtractInterfaceModel(state, target, new CodeBuilder()); Assert.AreEqual(ClassInstancing.Public, model.InterfaceInstancing); } } @@ -204,7 +204,7 @@ public void ExtractInterfaceRefactoring_DefaultsToPrivateInterfaceForNonExposedI .First(); //Specify Params to remove - var model = new ExtractInterfaceModel(state, target); + var model = new ExtractInterfaceModel(state, target, new CodeBuilder()); Assert.AreEqual(ClassInstancing.Private, model.InterfaceInstancing); } } @@ -309,10 +309,10 @@ protected override IRefactoring TestRefactoring(IRewritingManager rewritingManag uiDispatcherMock .Setup(m => m.Invoke(It.IsAny())) .Callback((Action action) => action.Invoke()); - var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager); + var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); var addComponentService = TestAddComponentService(state?.ProjectsProvider); var baseRefactoring = new ExtractInterfaceRefactoringAction(addImplementationsBaseRefactoring, state, state, rewritingManager, state?.ProjectsProvider, addComponentService); - return new ExtractInterfaceRefactoring(baseRefactoring, state, factory, selectionService, uiDispatcherMock.Object); + return new ExtractInterfaceRefactoring(baseRefactoring, state, factory, selectionService, uiDispatcherMock.Object, new CodeBuilder()); } private static IAddComponentService TestAddComponentService(IProjectsProvider projectsProvider) diff --git a/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceRefactoringActionTests.cs b/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceRefactoringActionTests.cs index 9ee9ca8c9f..f807a1cbc0 100644 --- a/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceRefactoringActionTests.cs @@ -772,7 +772,7 @@ private static ImplementInterfaceModel TestModel(RubberduckParserState state) protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) { - var addInterfaceImplementationsAction = new AddInterfaceImplementationsRefactoringAction(rewritingManager); + var addInterfaceImplementationsAction = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); return new ImplementInterfaceRefactoringAction(addInterfaceImplementationsAction, rewritingManager); } } diff --git a/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceTests.cs b/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceTests.cs index 01c271821c..aefaaa1aac 100644 --- a/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceTests.cs +++ b/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceTests.cs @@ -217,7 +217,7 @@ End Sub protected override IRefactoring TestRefactoring(IRewritingManager rewritingManager, RubberduckParserState state, ISelectionService selectionService) { - var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager); + var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); var baseRefactoring = new ImplementInterfaceRefactoringAction(addImplementationsBaseRefactoring, rewritingManager); return new ImplementInterfaceRefactoring(baseRefactoring, state, selectionService); } From 14abeaf53f4b490521f8db75524ec6d50822203d Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 1 Apr 2020 19:31:20 -0700 Subject: [PATCH 11/82] Cleanup from merge --- .../EncapsulateField/EncapsulateFieldRefactoring.cs | 7 ------- .../ExtractInterface/ExtractInterfaceRefactoring.cs | 9 --------- .../AddInterfaceImplementationsRefactoringAction.cs | 5 +---- RubberduckTests/CodeExplorer/MockedCodeExplorer.cs | 4 ---- .../RefactorCommands/EncapsulateFieldCommandTests.cs | 4 ---- .../RefactorCommands/ExtractInterfaceCommandTests.cs | 4 ---- .../EncapsulateField/EncapsulateFIeldTestSupport.cs | 8 -------- .../ExtractInterface/ExtractInterfaceTests.cs | 11 ----------- 8 files changed, 1 insertion(+), 51 deletions(-) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs index 68fe80ce5a..78003eddce 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs @@ -30,16 +30,9 @@ public class EncapsulateFieldRefactoring : InteractiveRefactoringBase userInteraction, IRewritingManager rewritingManager, ISelectionProvider selectionProvider, -//<<<<<<< HEAD -// ISelectedDeclarationProvider selectedDeclarationProvider, -// IUiDispatcher uiDispatcher, -// ICodeBuilder codeBuilder) -// :base(selectionProvider, factory, uiDispatcher) -//======= ISelectedDeclarationProvider selectedDeclarationProvider, ICodeBuilder codeBuilder) :base(selectionProvider, userInteraction) -//>>>>>>> rubberduck-vba/next { _declarationFinderProvider = declarationFinderProvider; _selectedDeclarationProvider = selectedDeclarationProvider; diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs index 08e1a24ac4..090c11a860 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoring.cs @@ -17,20 +17,11 @@ public class ExtractInterfaceRefactoring : InteractiveRefactoringBase userInteraction, ISelectionProvider selectionProvider, ICodeBuilder codeBuilder) :base(selectionProvider, userInteraction) -//>>>>>>> rubberduck-vba/next { _refactoringAction = refactoringAction; _declarationFinderProvider = declarationFinderProvider; diff --git a/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs b/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs index 796bf44bfe..df09076306 100644 --- a/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs +++ b/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs @@ -4,10 +4,7 @@ using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; -//<<<<<<< HEAD -//======= -//using Rubberduck.Refactorings.Common; -//>>>>>>> rubberduck-vba/next + namespace Rubberduck.Refactorings.AddInterfaceImplementations { diff --git a/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs b/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs index c77f3200ed..be7d89cc4b 100644 --- a/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs +++ b/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs @@ -510,11 +510,7 @@ public MockedCodeExplorer ImplementExtractInterfaceCommand() var extractInterfaceBaseRefactoring = new ExtractInterfaceRefactoringAction(addImplementationsBaseRefactoring, State, State, null, State.ProjectsProvider, addComponentService); var userInteraction = new RefactoringUserInteraction(null, _uiDispatcher.Object); ViewModel.CodeExplorerExtractInterfaceCommand = new CodeExplorerExtractInterfaceCommand( -//<<<<<<< HEAD -// new ExtractInterfaceRefactoring(extractInterfaceBaseRefactoring, State, null, null, _uiDispatcher.Object, new CodeBuilder()), -//======= new ExtractInterfaceRefactoring(extractInterfaceBaseRefactoring, State, userInteraction, null, new CodeBuilder()), -//>>>>>>> rubberduck-vba/next State, null, VbeEvents.Object); return this; } diff --git a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs index 3b4b9452da..2d9a377960 100644 --- a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs +++ b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs @@ -63,12 +63,8 @@ protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state uiDispatcherMock .Setup(m => m.Invoke(It.IsAny())) .Callback((Action action) => action.Invoke()); -//<<<<<<< HEAD -// var refactoring = new EncapsulateFieldRefactoring(state, null, factory, rewritingManager, selectionService, selectedDeclarationProvider, uiDispatcherMock.Object, new CodeBuilder()); -//======= var userInteraction = new RefactoringUserInteraction(factory, uiDispatcherMock.Object); var refactoring = new EncapsulateFieldRefactoring(state, null, userInteraction, rewritingManager, selectionService, selectedDeclarationProvider, new CodeBuilder()); -//>>>>>>> rubberduck-vba/next var notifier = new EncapsulateFieldFailedNotifier(msgBox); var selectedDeclarationService = new SelectedDeclarationProvider(selectionService, state); return new RefactorEncapsulateFieldCommand(refactoring, notifier, state, selectionService, selectedDeclarationService); diff --git a/RubberduckTests/Commands/RefactorCommands/ExtractInterfaceCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/ExtractInterfaceCommandTests.cs index 017249e26f..d0678ec0bf 100644 --- a/RubberduckTests/Commands/RefactorCommands/ExtractInterfaceCommandTests.cs +++ b/RubberduckTests/Commands/RefactorCommands/ExtractInterfaceCommandTests.cs @@ -176,12 +176,8 @@ protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); var addComponentService = TestAddComponentService(state.ProjectsProvider); var baseRefactoring = new ExtractInterfaceRefactoringAction(addImplementationsBaseRefactoring, state, state, rewritingManager, state.ProjectsProvider, addComponentService); -//<<<<<<< HEAD -// var refactoring = new ExtractInterfaceRefactoring(baseRefactoring, state, factory, selectionService, uiDispatcherMock.Object, new CodeBuilder()); -//======= var userInteraction = new RefactoringUserInteraction(factory, uiDispatcherMock.Object); var refactoring = new ExtractInterfaceRefactoring(baseRefactoring, state, userInteraction, selectionService, new CodeBuilder()); -//>>>>>>> rubberduck-vba/next var notifier = new ExtractInterfaceFailedNotifier(msgBox); return new RefactorExtractInterfaceCommand(refactoring, notifier, state, selectionService); } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs index aa9f8ea9cd..b7878733e0 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs @@ -119,15 +119,7 @@ public string RefactoredCode(CodeString codeString, Func(); -// uiDispatcherMock -// .Setup(m => m.Invoke(It.IsAny())) -// .Callback((Action action) => action.Invoke()); -// return new EncapsulateFieldRefactoring(state, indenter, factory, rewritingManager, selectionService, selectedDeclarationProvider, uiDispatcherMock.Object, new CodeBuilder()); -//======= return new EncapsulateFieldRefactoring(state, indenter, userInteraction, rewritingManager, selectionService, selectedDeclarationProvider, new CodeBuilder()); -//>>>>>>> rubberduck-vba/next } public IEncapsulateFieldCandidate RetrieveEncapsulateFieldCandidate(string inputCode, string fieldName) diff --git a/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceTests.cs b/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceTests.cs index 0d09633286..47970b8836 100644 --- a/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceTests.cs +++ b/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceTests.cs @@ -309,21 +309,10 @@ End Sub RefactoringUserInteraction userInteraction, ISelectionService selectionService) { -//<<<<<<< HEAD -// var uiDispatcherMock = new Mock(); -// uiDispatcherMock -// .Setup(m => m.Invoke(It.IsAny())) -// .Callback((Action action) => action.Invoke()); -// var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); -// var addComponentService = TestAddComponentService(state?.ProjectsProvider); -// var baseRefactoring = new ExtractInterfaceRefactoringAction(addImplementationsBaseRefactoring, state, state, rewritingManager, state?.ProjectsProvider, addComponentService); -// return new ExtractInterfaceRefactoring(baseRefactoring, state, factory, selectionService, uiDispatcherMock.Object, new CodeBuilder()); -//======= var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); var addComponentService = TestAddComponentService(state?.ProjectsProvider); var baseRefactoring = new ExtractInterfaceRefactoringAction(addImplementationsBaseRefactoring, state, state, rewritingManager, state?.ProjectsProvider, addComponentService); return new ExtractInterfaceRefactoring(baseRefactoring, state, userInteraction, selectionService, new CodeBuilder()); -//>>>>>>> rubberduck-vba/next } private static IAddComponentService TestAddComponentService(IProjectsProvider projectsProvider) From 6710770530a405e5e1a633b16aaa26c123a35e0c Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Thu, 16 Apr 2020 00:17:30 +0200 Subject: [PATCH 12/82] Add allowed argument types to IAnnotation There has to be at least one per required argument. All optional arguments have the last allowed type in the list. --- .../UI/Controls/FindAllReferencesService.cs | 10 +++++-- .../Annotations/AnnotationBase.cs | 4 ++- .../Concrete/DefaultMemberAnnotation.cs | 7 +---- .../Concrete/DescriptionAnnotation.cs | 8 ++--- .../DescriptionAttributeAnnotationBase.cs | 9 ++---- .../Concrete/EnumeratorMemberAnnotation.cs | 3 -- .../Concrete/ExcelHotKeyAnnotation.cs | 9 ++---- .../Concrete/ExposedModuleAnnotation.cs | 4 --- .../FlexibleAttributeAnnotationBase.cs | 4 +-- .../FlexibleAttributeValueAnnotationBase.cs | 2 +- .../Annotations/Concrete/FolderAnnotation.cs | 2 +- .../Annotations/Concrete/IgnoreAnnotation.cs | 2 +- .../Concrete/IgnoreModuleAnnotation.cs | 2 +- .../Concrete/IgnoreTestAnnotation.cs | 2 +- .../Concrete/MemberAttributeAnnotation.cs | 14 +++++---- .../Concrete/ModuleAttributeAnnotation.cs | 14 +++++---- .../Concrete/ModuleDescriptionAnnotation.cs | 8 ++--- .../Concrete/ObsoleteAnnotation.cs | 3 +- .../Concrete/PredeclaredIdAnnotation.cs | 4 --- .../Concrete/TestMethodAnnotation.cs | 3 +- .../Concrete/VariableDescriptionAnnotation.cs | 8 ++--- Rubberduck.Parsing/Annotations/IAnnotation.cs | 30 +++++++++++++++++++ 22 files changed, 78 insertions(+), 74 deletions(-) diff --git a/Rubberduck.Core/UI/Controls/FindAllReferencesService.cs b/Rubberduck.Core/UI/Controls/FindAllReferencesService.cs index c515d303c1..a2e25530e0 100644 --- a/Rubberduck.Core/UI/Controls/FindAllReferencesService.cs +++ b/Rubberduck.Core/UI/Controls/FindAllReferencesService.cs @@ -22,9 +22,13 @@ public class FindAllReferencesService : IDisposable private readonly SearchResultPresenterInstanceManager _presenterService; private readonly IUiDispatcher _uiDispatcher; - public FindAllReferencesService(INavigateCommand navigateCommand, IMessageBox messageBox, - RubberduckParserState state, ISearchResultsWindowViewModel viewModel, - SearchResultPresenterInstanceManager presenterService, IUiDispatcher uiDispatcher) + public FindAllReferencesService( + INavigateCommand navigateCommand, + IMessageBox messageBox, + RubberduckParserState state, + ISearchResultsWindowViewModel viewModel, + SearchResultPresenterInstanceManager presenterService, + IUiDispatcher uiDispatcher) { _navigateCommand = navigateCommand; _messageBox = messageBox; diff --git a/Rubberduck.Parsing/Annotations/AnnotationBase.cs b/Rubberduck.Parsing/Annotations/AnnotationBase.cs index 52614ec88e..6e598c42a4 100644 --- a/Rubberduck.Parsing/Annotations/AnnotationBase.cs +++ b/Rubberduck.Parsing/Annotations/AnnotationBase.cs @@ -8,16 +8,18 @@ public abstract class AnnotationBase : IAnnotation public bool AllowMultiple { get; } public int RequiredArguments { get; } public int? AllowedArguments { get; } + public IReadOnlyList AllowedArgumentTypes { get; } public string Name { get; } public AnnotationTarget Target { get; } - protected AnnotationBase(string name, AnnotationTarget target, int requiredArguments = 0, int? allowedArguments = 0, bool allowMultiple = false) + protected AnnotationBase(string name, AnnotationTarget target, int requiredArguments = 0, int? allowedArguments = 0, IReadOnlyList allowedArgumentTypes = null, bool allowMultiple = false) { Name = name; Target = target; AllowMultiple = allowMultiple; RequiredArguments = requiredArguments; AllowedArguments = allowedArguments; + AllowedArgumentTypes = allowedArgumentTypes ?? new List(); } public virtual IReadOnlyList ProcessAnnotationArguments(IEnumerable arguments) diff --git a/Rubberduck.Parsing/Annotations/Concrete/DefaultMemberAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/DefaultMemberAnnotation.cs index 090ffa005e..c7c201579d 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/DefaultMemberAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/DefaultMemberAnnotation.cs @@ -1,7 +1,3 @@ -using System.Collections.Generic; -using System.Linq; -using Rubberduck.Parsing.Grammar; -using Rubberduck.VBEditor; using Rubberduck.Resources.Registration; namespace Rubberduck.Parsing.Annotations @@ -13,7 +9,6 @@ public sealed class DefaultMemberAnnotation : FixedAttributeValueAnnotationBase { public DefaultMemberAnnotation() : base("DefaultMember", AnnotationTarget.Member, "VB_UserMemId", new[] { WellKnownDispIds.Value.ToString() }) - { - } + {} } } diff --git a/Rubberduck.Parsing/Annotations/Concrete/DescriptionAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/DescriptionAnnotation.cs index 1bbed06265..84073184a4 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/DescriptionAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/DescriptionAnnotation.cs @@ -1,8 +1,4 @@ -using System.Collections.Generic; -using Rubberduck.Parsing.Grammar; -using Rubberduck.VBEditor; - -namespace Rubberduck.Parsing.Annotations +namespace Rubberduck.Parsing.Annotations { /// /// Used for specifying a member's VB_Description attribute. @@ -10,7 +6,7 @@ namespace Rubberduck.Parsing.Annotations public sealed class DescriptionAnnotation : DescriptionAttributeAnnotationBase { public DescriptionAnnotation() - : base("Description", AnnotationTarget.Member, "VB_Description", 1) + : base("Description", AnnotationTarget.Member, "VB_Description") {} } } \ No newline at end of file diff --git a/Rubberduck.Parsing/Annotations/Concrete/DescriptionAttributeAnnotationBase.cs b/Rubberduck.Parsing/Annotations/Concrete/DescriptionAttributeAnnotationBase.cs index 6c42023eea..dea0226f3f 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/DescriptionAttributeAnnotationBase.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/DescriptionAttributeAnnotationBase.cs @@ -1,14 +1,11 @@ using System.Collections.Generic; -using System.Linq; -using Rubberduck.Parsing.Grammar; -using Rubberduck.VBEditor; namespace Rubberduck.Parsing.Annotations { public abstract class DescriptionAttributeAnnotationBase : FlexibleAttributeValueAnnotationBase { - public DescriptionAttributeAnnotationBase(string name, AnnotationTarget target, string attribute, int valueCount) - : base(name, target, attribute, valueCount) - { } + public DescriptionAttributeAnnotationBase(string name, AnnotationTarget target, string attribute) + : base(name, target, attribute, 1, new List { AnnotationArgumentType.Text }) + {} } } \ No newline at end of file diff --git a/Rubberduck.Parsing/Annotations/Concrete/EnumeratorMemberAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/EnumeratorMemberAnnotation.cs index 22bfc8bca0..b872fbfb87 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/EnumeratorMemberAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/EnumeratorMemberAnnotation.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using Rubberduck.Parsing.Grammar; -using Rubberduck.VBEditor; using Rubberduck.Resources.Registration; namespace Rubberduck.Parsing.Annotations diff --git a/Rubberduck.Parsing/Annotations/Concrete/ExcelHotKeyAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/ExcelHotKeyAnnotation.cs index b1bdcce09b..f43b75f624 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/ExcelHotKeyAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/ExcelHotKeyAnnotation.cs @@ -1,17 +1,14 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Rubberduck.Common; -using Rubberduck.Parsing.Grammar; -using Rubberduck.VBEditor; namespace Rubberduck.Parsing.Annotations { public sealed class ExcelHotKeyAnnotation : FlexibleAttributeValueAnnotationBase { public ExcelHotKeyAnnotation() - : base("ExcelHotkey", AnnotationTarget.Member, "VB_ProcData.VB_Invoke_Func", 1) - { } + : base("ExcelHotkey", AnnotationTarget.Member, "VB_ProcData.VB_Invoke_Func", 1, new[] { AnnotationArgumentType.Text}) + {} public override IReadOnlyList AnnotationToAttributeValues(IReadOnlyList annotationValues) { diff --git a/Rubberduck.Parsing/Annotations/Concrete/ExposedModuleAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/ExposedModuleAnnotation.cs index 3ba70ee34f..cb73fe5adc 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/ExposedModuleAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/ExposedModuleAnnotation.cs @@ -1,7 +1,3 @@ -using System.Collections.Generic; -using Rubberduck.Parsing.Grammar; -using Rubberduck.VBEditor; - namespace Rubberduck.Parsing.Annotations { /// diff --git a/Rubberduck.Parsing/Annotations/Concrete/FlexibleAttributeAnnotationBase.cs b/Rubberduck.Parsing/Annotations/Concrete/FlexibleAttributeAnnotationBase.cs index dca41c6827..c19d11175f 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/FlexibleAttributeAnnotationBase.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/FlexibleAttributeAnnotationBase.cs @@ -6,8 +6,8 @@ namespace Rubberduck.Parsing.Annotations { public abstract class FlexibleAttributeAnnotationBase : AnnotationBase, IAttributeAnnotation { - protected FlexibleAttributeAnnotationBase(string name, AnnotationTarget target, bool allowMultiple = false) - : base(name, target, 2, null, allowMultiple) //We need at least the attribute name and one value for it. + protected FlexibleAttributeAnnotationBase(string name, AnnotationTarget target, IReadOnlyList allowedArgumentType, bool allowMultiple = false) + : base(name, target, 2, null, allowedArgumentType, allowMultiple) //We need at least the attribute name and one value for it. {} public IReadOnlyList AnnotationToAttributeValues(IReadOnlyList annotationValues) diff --git a/Rubberduck.Parsing/Annotations/Concrete/FlexibleAttributeValueAnnotationBase.cs b/Rubberduck.Parsing/Annotations/Concrete/FlexibleAttributeValueAnnotationBase.cs index 141e4f5b87..c68207e23c 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/FlexibleAttributeValueAnnotationBase.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/FlexibleAttributeValueAnnotationBase.cs @@ -9,7 +9,7 @@ public abstract class FlexibleAttributeValueAnnotationBase : AnnotationBase, IAt private readonly string _attribute; private readonly int _numberOfValues; - protected FlexibleAttributeValueAnnotationBase(string name, AnnotationTarget target, string attribute, int numberOfValues) + protected FlexibleAttributeValueAnnotationBase(string name, AnnotationTarget target, string attribute, int numberOfValues, IReadOnlyList argumentTypes) : base(name, target, numberOfValues, numberOfValues) { _attribute = attribute; diff --git a/Rubberduck.Parsing/Annotations/Concrete/FolderAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/FolderAnnotation.cs index ea8200337f..f72e40c9b8 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/FolderAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/FolderAnnotation.cs @@ -6,7 +6,7 @@ public sealed class FolderAnnotation : AnnotationBase { public FolderAnnotation() - : base("Folder", AnnotationTarget.Module, 1, 1) + : base("Folder", AnnotationTarget.Module, 1, 1, new[] { AnnotationArgumentType.Text}) {} } } diff --git a/Rubberduck.Parsing/Annotations/Concrete/IgnoreAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/IgnoreAnnotation.cs index 736a0d6b3a..41ee1731f2 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/IgnoreAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/IgnoreAnnotation.cs @@ -6,7 +6,7 @@ public sealed class IgnoreAnnotation : AnnotationBase { public IgnoreAnnotation() - : base("Ignore", AnnotationTarget.General, 1, null, true) + : base("Ignore", AnnotationTarget.General, 1, null, new[] { AnnotationArgumentType.Inspection}, true) {} } } diff --git a/Rubberduck.Parsing/Annotations/Concrete/IgnoreModuleAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/IgnoreModuleAnnotation.cs index 70c9a3902e..6f6a108401 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/IgnoreModuleAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/IgnoreModuleAnnotation.cs @@ -6,7 +6,7 @@ public sealed class IgnoreModuleAnnotation : AnnotationBase { public IgnoreModuleAnnotation() - : base("IgnoreModule", AnnotationTarget.Module, allowedArguments: null, allowMultiple: true) + : base("IgnoreModule", AnnotationTarget.Module, allowedArguments: null, allowedArgumentTypes: new[] { AnnotationArgumentType.Inspection }, allowMultiple: true) {} } } \ No newline at end of file diff --git a/Rubberduck.Parsing/Annotations/Concrete/IgnoreTestAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/IgnoreTestAnnotation.cs index 0a788bdecd..b5addde663 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/IgnoreTestAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/IgnoreTestAnnotation.cs @@ -6,7 +6,7 @@ public sealed class IgnoreTestAnnotation : AnnotationBase { public IgnoreTestAnnotation() - : base("IgnoreTest", AnnotationTarget.Member, allowedArguments: 1) + : base("IgnoreTest", AnnotationTarget.Member, allowedArguments: 1, allowedArgumentTypes: new []{AnnotationArgumentType.Text}) {} } } \ No newline at end of file diff --git a/Rubberduck.Parsing/Annotations/Concrete/MemberAttributeAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/MemberAttributeAnnotation.cs index 725646c3f3..b0d785818b 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/MemberAttributeAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/MemberAttributeAnnotation.cs @@ -1,8 +1,4 @@ -using System.Collections.Generic; -using Rubberduck.Parsing.Grammar; -using Rubberduck.VBEditor; - -namespace Rubberduck.Parsing.Annotations +namespace Rubberduck.Parsing.Annotations { /// /// This annotation allows the specification of arbitrary VB_Attribute entries for members. @@ -14,7 +10,13 @@ namespace Rubberduck.Parsing.Annotations public class MemberAttributeAnnotation : FlexibleAttributeAnnotationBase { public MemberAttributeAnnotation() - : base("MemberAttribute", AnnotationTarget.Member | AnnotationTarget.Variable) + : base("MemberAttribute", AnnotationTarget.Member | AnnotationTarget.Variable, _argumentTypes) {} + + private static AnnotationArgumentType[] _argumentTypes = new[] + { + AnnotationArgumentType.Attribute, + AnnotationArgumentType.Text | AnnotationArgumentType.Number | AnnotationArgumentType.Boolean + }; } } \ No newline at end of file diff --git a/Rubberduck.Parsing/Annotations/Concrete/ModuleAttributeAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/ModuleAttributeAnnotation.cs index 55b44a5397..58dae59764 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/ModuleAttributeAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/ModuleAttributeAnnotation.cs @@ -1,8 +1,4 @@ -using System.Collections.Generic; -using Rubberduck.Parsing.Grammar; -using Rubberduck.VBEditor; - -namespace Rubberduck.Parsing.Annotations +namespace Rubberduck.Parsing.Annotations { /// /// This annotation allows specifying arbitrary VB_Attribute entries. @@ -10,7 +6,13 @@ namespace Rubberduck.Parsing.Annotations public class ModuleAttributeAnnotation : FlexibleAttributeAnnotationBase { public ModuleAttributeAnnotation() - : base("ModuleAttribute", AnnotationTarget.Module) + : base("ModuleAttribute", AnnotationTarget.Module, _argumentTypes) {} + + private static AnnotationArgumentType[] _argumentTypes = new[] + { + AnnotationArgumentType.Attribute, + AnnotationArgumentType.Text | AnnotationArgumentType.Number | AnnotationArgumentType.Boolean + }; } } \ No newline at end of file diff --git a/Rubberduck.Parsing/Annotations/Concrete/ModuleDescriptionAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/ModuleDescriptionAnnotation.cs index d44c0702ca..ff5122c80d 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/ModuleDescriptionAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/ModuleDescriptionAnnotation.cs @@ -1,8 +1,4 @@ -using System.Collections.Generic; -using Rubberduck.Parsing.Grammar; -using Rubberduck.VBEditor; - -namespace Rubberduck.Parsing.Annotations +namespace Rubberduck.Parsing.Annotations { /// /// Used for specifying a module's VB_Description attribute. @@ -13,7 +9,7 @@ namespace Rubberduck.Parsing.Annotations public sealed class ModuleDescriptionAnnotation : DescriptionAttributeAnnotationBase { public ModuleDescriptionAnnotation() - : base("ModuleDescription", AnnotationTarget.Module, "VB_Description", 1) + : base("ModuleDescription", AnnotationTarget.Module, "VB_Description") {} } } \ No newline at end of file diff --git a/Rubberduck.Parsing/Annotations/Concrete/ObsoleteAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/ObsoleteAnnotation.cs index d029d2234a..b074220d71 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/ObsoleteAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/ObsoleteAnnotation.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Rubberduck.Common; namespace Rubberduck.Parsing.Annotations { @@ -12,7 +11,7 @@ public sealed class ObsoleteAnnotation : AnnotationBase public string ReplacementDocumentation { get; private set; } public ObsoleteAnnotation() - : base("Obsolete", AnnotationTarget.Member | AnnotationTarget.Variable, allowedArguments: 1) + : base("Obsolete", AnnotationTarget.Member | AnnotationTarget.Variable, allowedArguments: 1, allowedArgumentTypes: new [] {AnnotationArgumentType.Text}) {} public override IReadOnlyList ProcessAnnotationArguments(IEnumerable arguments) diff --git a/Rubberduck.Parsing/Annotations/Concrete/PredeclaredIdAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/PredeclaredIdAnnotation.cs index 4216718479..5e599c3701 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/PredeclaredIdAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/PredeclaredIdAnnotation.cs @@ -1,7 +1,3 @@ -using System.Collections.Generic; -using Rubberduck.Parsing.Grammar; -using Rubberduck.VBEditor; - namespace Rubberduck.Parsing.Annotations { /// diff --git a/Rubberduck.Parsing/Annotations/Concrete/TestMethodAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/TestMethodAnnotation.cs index 4d83fa8cd5..19776b23cd 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/TestMethodAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/TestMethodAnnotation.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Rubberduck.Common; namespace Rubberduck.Parsing.Annotations { @@ -10,7 +9,7 @@ namespace Rubberduck.Parsing.Annotations public sealed class TestMethodAnnotation : AnnotationBase, ITestAnnotation { public TestMethodAnnotation() - : base("TestMethod", AnnotationTarget.Member) + : base("TestMethod", AnnotationTarget.Member, allowedArguments: 1, allowedArgumentTypes: new []{AnnotationArgumentType.Text}) {} public IReadOnlyList ProcessAnnotationArguments(IEnumerable arguments) diff --git a/Rubberduck.Parsing/Annotations/Concrete/VariableDescriptionAnnotation.cs b/Rubberduck.Parsing/Annotations/Concrete/VariableDescriptionAnnotation.cs index 7d4297dff6..6925948a69 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/VariableDescriptionAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/VariableDescriptionAnnotation.cs @@ -1,13 +1,9 @@ -using System.Collections.Generic; -using Rubberduck.Parsing.Grammar; -using Rubberduck.VBEditor; - -namespace Rubberduck.Parsing.Annotations +namespace Rubberduck.Parsing.Annotations { public class VariableDescriptionAnnotation : DescriptionAttributeAnnotationBase { public VariableDescriptionAnnotation() - : base("VariableDescription", AnnotationTarget.Variable, "VB_VarDescription", 1) + : base("VariableDescription", AnnotationTarget.Variable, "VB_VarDescription") {} } } \ No newline at end of file diff --git a/Rubberduck.Parsing/Annotations/IAnnotation.cs b/Rubberduck.Parsing/Annotations/IAnnotation.cs index d205abf1ae..b7a2003d2b 100644 --- a/Rubberduck.Parsing/Annotations/IAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/IAnnotation.cs @@ -27,6 +27,11 @@ public interface IAnnotation /// int? AllowedArguments { get; } + /// + /// The allowed types of arguments for the annotation. The last argument type is valid for all optional arguments. + /// + IReadOnlyList AllowedArgumentTypes { get; } + IReadOnlyList ProcessAnnotationArguments(IEnumerable arguments); } @@ -54,4 +59,29 @@ public enum AnnotationTarget /// General = Member | Variable | Identifier, } + + [Flags] + public enum AnnotationArgumentType + { + /// + /// Indicates that the annotation argument can be a string. + /// + Text = 1 << 0, + /// + /// Indicates that the annotation argument can be a number. + /// + Number = 1 << 1, + /// + /// Indicates that the annotation argument can be a boolean. + /// + Boolean = 1 << 2, + /// + /// Indicates that the annotation argument can be an inspection name. + /// + Inspection = 1 << 3, + /// + /// Indicates that the annotation argument can be an attribute name. + /// + Attribute = 1 << 4 + } } From 9ba3d0f3daa4613b9c97fb2c188ece78b091f582 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Fri, 24 Apr 2020 19:52:35 +0200 Subject: [PATCH 13/82] Introduce AnnotateDeclarationRefactoringAction --- .../AnnotateDeclarationModel.cs | 36 +++ .../AnnotateDeclarationRefactoring.cs | 46 +++ .../AnnotateFolderRefactoringAction.cs | 55 ++++ .../IAnnotateDeclarationPresenter.cs | 5 + .../MoveToFolderRefactoringAction.cs | 4 +- ...notateDeclarationRefactoringActionTests.cs | 283 ++++++++++++++++++ .../AnnotateDeclarationRefactoringTests.cs | 79 +++++ .../MoveToFolderRefactoringTests.cs | 1 - 8 files changed, 505 insertions(+), 4 deletions(-) create mode 100644 Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationModel.cs create mode 100644 Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationRefactoring.cs create mode 100644 Rubberduck.Refactorings/AnnotateDeclaration/AnnotateFolderRefactoringAction.cs create mode 100644 Rubberduck.Refactorings/AnnotateDeclaration/IAnnotateDeclarationPresenter.cs create mode 100644 RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringActionTests.cs create mode 100644 RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringTests.cs diff --git a/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationModel.cs b/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationModel.cs new file mode 100644 index 0000000000..7af819c312 --- /dev/null +++ b/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationModel.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.Symbols; + +namespace Rubberduck.Refactorings.AnnotateDeclaration +{ + //This is introduced in favor of a value tuple in order to ba able to bind to the components in XAML. + public struct TypedAnnotationArgument + { + public AnnotationArgumentType ArgumentType { get; set; } + public string Argument { get; set; } + + public TypedAnnotationArgument(AnnotationArgumentType type, string argument) + { + ArgumentType = type; + Argument = argument; + } + } + + public class AnnotateDeclarationModel : IRefactoringModel + { + public Declaration Target { get; } + public IAnnotation Annotation { get; set; } + public IList Arguments { get; } + + public AnnotateDeclarationModel( + Declaration target, + IAnnotation annotation = null, + IList arguments = null) + { + Target = target; + Annotation = annotation; + Arguments = arguments ?? new List(); + } + } +} \ No newline at end of file diff --git a/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationRefactoring.cs b/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationRefactoring.cs new file mode 100644 index 0000000000..054ee737e6 --- /dev/null +++ b/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationRefactoring.cs @@ -0,0 +1,46 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.Exceptions; +using Rubberduck.VBEditor; +using Rubberduck.VBEditor.Utility; + +namespace Rubberduck.Refactorings.AnnotateDeclaration +{ + public class AnnotateDeclarationRefactoring : InteractiveRefactoringBase + { + private readonly IRefactoringAction _annotateDeclarationAction; + private readonly ISelectedDeclarationProvider _selectedDeclarationProvider; + + public AnnotateDeclarationRefactoring( + AnnotateDeclarationRefactoringAction annotateDeclarationAction, + ISelectedDeclarationProvider selectedDeclarationProvider, + ISelectionProvider selectionProvider, + RefactoringUserInteraction userInteraction) + : base(selectionProvider, userInteraction) + { + _annotateDeclarationAction = annotateDeclarationAction; + _selectedDeclarationProvider = selectedDeclarationProvider; + } + + protected override Declaration FindTargetDeclaration(QualifiedSelection targetSelection) + { + return _selectedDeclarationProvider.SelectedModule(targetSelection); + } + + protected override AnnotateDeclarationModel InitializeModel(Declaration target) + { + if (target == null) + { + throw new TargetDeclarationIsNullException(); + } + + return new AnnotateDeclarationModel(target); + } + + protected override void RefactorImpl(AnnotateDeclarationModel model) + { + _annotateDeclarationAction.Refactor(model); + } + } +} + diff --git a/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateFolderRefactoringAction.cs b/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateFolderRefactoringAction.cs new file mode 100644 index 0000000000..b58ab1b63c --- /dev/null +++ b/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateFolderRefactoringAction.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using Rubberduck.Common; +using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.VBA; + +namespace Rubberduck.Refactorings.AnnotateDeclaration +{ + public class AnnotateDeclarationRefactoringAction : CodeOnlyRefactoringActionBase + { + private readonly IAnnotationUpdater _annotationUpdater; + + public AnnotateDeclarationRefactoringAction( + IRewritingManager rewritingManager, + IAnnotationUpdater annotationUpdater) + : base(rewritingManager) + { + _annotationUpdater = annotationUpdater; + } + + public override void Refactor(AnnotateDeclarationModel model, IRewriteSession rewriteSession) + { + var arguments = model.Arguments.Select(ToCode).ToList(); + _annotationUpdater.AddAnnotation(rewriteSession, model.Target, model.Annotation, arguments); + } + + private string ToCode(TypedAnnotationArgument annotationArgument) + { + switch (annotationArgument.ArgumentType) + { + case AnnotationArgumentType.Text: + return annotationArgument.Argument.ToVbaStringLiteral(); + case AnnotationArgumentType.Boolean: + return ToBooleanLiteral(annotationArgument.Argument); + default: + return annotationArgument.Argument; + } + } + + private const string NotABoolean = "NOT_A_BOOLEAN"; + private string ToBooleanLiteral(string booleanText) + { + if (!bool.TryParse(booleanText, out var booleanValue)) + { + return NotABoolean; + } + + return booleanValue + ? Tokens.True + : Tokens.False; + } + } +} \ No newline at end of file diff --git a/Rubberduck.Refactorings/AnnotateDeclaration/IAnnotateDeclarationPresenter.cs b/Rubberduck.Refactorings/AnnotateDeclaration/IAnnotateDeclarationPresenter.cs new file mode 100644 index 0000000000..38464194e8 --- /dev/null +++ b/Rubberduck.Refactorings/AnnotateDeclaration/IAnnotateDeclarationPresenter.cs @@ -0,0 +1,5 @@ +namespace Rubberduck.Refactorings.AnnotateDeclaration +{ + public interface IAnnotateDeclarationPresenter : IRefactoringPresenter + {} +} \ No newline at end of file diff --git a/Rubberduck.Refactorings/MoveToFolder/MoveToFolderRefactoringAction.cs b/Rubberduck.Refactorings/MoveToFolder/MoveToFolderRefactoringAction.cs index 38708217a4..ca35dd0b43 100644 --- a/Rubberduck.Refactorings/MoveToFolder/MoveToFolderRefactoringAction.cs +++ b/Rubberduck.Refactorings/MoveToFolder/MoveToFolderRefactoringAction.cs @@ -47,9 +47,7 @@ private void UpdateFolderAnnotation(MoveToFolderModel model, IParseTreeAnnotatio private static (IAnnotation annotation, IReadOnlyList annotationArguments) NewAnnotation(string targetFolder) { - var targetFolderLiteral = targetFolder - .Replace("\"", "\"\"") - .EnQuote(); + var targetFolderLiteral = targetFolder.ToVbaStringLiteral(); var annotation = new FolderAnnotation(); var annotationValues = new List { targetFolderLiteral }; diff --git a/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringActionTests.cs b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringActionTests.cs new file mode 100644 index 0000000000..e7924124be --- /dev/null +++ b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringActionTests.cs @@ -0,0 +1,283 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.AnnotateDeclaration; + +namespace RubberduckTests.Refactoring.AnnotateDeclaration +{ + [TestFixture] + public class AnnotateDeclarationRefactoringActionTests : RefactoringActionTestBase + { + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoringAction_NoArgument() + { + const string code = @" +Public Sub Foo() +End Sub +"; + const string expectedCode = @"'@Exposed + +Public Sub Foo() +End Sub +"; + Func modelBuilder = (state) => + { + var module = state.DeclarationFinder + .UserDeclarations(DeclarationType.ProceduralModule) + .Single(); + var annotation = new ExposedModuleAnnotation(); + var arguments = new List(); + + return new AnnotateDeclarationModel(module, annotation, arguments); + }; + + var refactoredCode = RefactoredCode(code, modelBuilder); + + Assert.AreEqual(expectedCode, refactoredCode); + } + + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoringAction_TextArgument() + { + const string code = @" +Public Sub Foo() +End Sub +"; + const string expectedCode = @"'@Folder ""MyNew""""Folder.MySubFolder"" + +Public Sub Foo() +End Sub +"; + Func modelBuilder = (state) => + { + var module = state.DeclarationFinder + .UserDeclarations(DeclarationType.ProceduralModule) + .Single(); + var annotation = new FolderAnnotation(); + var arguments = new List + { + new TypedAnnotationArgument(AnnotationArgumentType.Text, "MyNew\"Folder.MySubFolder") + }; + + return new AnnotateDeclarationModel(module, annotation, arguments); + }; + + var refactoredCode = RefactoredCode(code, modelBuilder); + + Assert.AreEqual(expectedCode, refactoredCode); + } + + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoringAction_InspectionNameArgument() + { + const string code = @" +Public Sub Foo() +End Sub +"; + const string expectedCode = @"'@IgnoreModule AssignmentNotUsed, FunctionReturnValueNotUsed + +Public Sub Foo() +End Sub +"; + Func modelBuilder = (state) => + { + var module = state.DeclarationFinder + .UserDeclarations(DeclarationType.ProceduralModule) + .Single(); + var annotation = new IgnoreModuleAnnotation(); + var arguments = new List + { + new TypedAnnotationArgument(AnnotationArgumentType.Inspection, "AssignmentNotUsed"), + new TypedAnnotationArgument(AnnotationArgumentType.Inspection, "FunctionReturnValueNotUsed") + }; + + return new AnnotateDeclarationModel(module, annotation, arguments); + }; + + var refactoredCode = RefactoredCode(code, modelBuilder); + + Assert.AreEqual(expectedCode, refactoredCode); + } + + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoringAction_AttributeArgument() + { + const string code = @" +Public Sub Foo() +End Sub +"; + const string expectedCode = @"'@ModuleAttribute VB_Description, ""MyDescription"" + +Public Sub Foo() +End Sub +"; + Func modelBuilder = (state) => + { + var module = state.DeclarationFinder + .UserDeclarations(DeclarationType.ProceduralModule) + .Single(); + var annotation = new ModuleAttributeAnnotation(); + var arguments = new List + { + new TypedAnnotationArgument(AnnotationArgumentType.Attribute, "VB_Description"), + new TypedAnnotationArgument(AnnotationArgumentType.Text, "MyDescription") + }; + + return new AnnotateDeclarationModel(module, annotation, arguments); + }; + + var refactoredCode = RefactoredCode(code, modelBuilder); + + Assert.AreEqual(expectedCode, refactoredCode); + } + + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoringAction_BooleanArgument_True() + { + const string code = @" +Public Sub Foo() +End Sub +"; + const string expectedCode = @"'@ModuleAttribute VB_Exposed, True + +Public Sub Foo() +End Sub +"; + Func modelBuilder = (state) => + { + var module = state.DeclarationFinder + .UserDeclarations(DeclarationType.ProceduralModule) + .Single(); + var annotation = new ModuleAttributeAnnotation(); + var arguments = new List + { + new TypedAnnotationArgument(AnnotationArgumentType.Attribute, "VB_Exposed"), + new TypedAnnotationArgument(AnnotationArgumentType.Boolean, "true") + }; + + return new AnnotateDeclarationModel(module, annotation, arguments); + }; + + var refactoredCode = RefactoredCode(code, modelBuilder); + + Assert.AreEqual(expectedCode, refactoredCode); + } + + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoringAction_BooleanArgument_False() + { + const string code = @" +Public Sub Foo() +End Sub +"; + const string expectedCode = @"'@ModuleAttribute VB_Exposed, False + +Public Sub Foo() +End Sub +"; + Func modelBuilder = (state) => + { + var module = state.DeclarationFinder + .UserDeclarations(DeclarationType.ProceduralModule) + .Single(); + var annotation = new ModuleAttributeAnnotation(); + var arguments = new List + { + new TypedAnnotationArgument(AnnotationArgumentType.Attribute, "VB_Exposed"), + new TypedAnnotationArgument(AnnotationArgumentType.Boolean, "faLse") + }; + + return new AnnotateDeclarationModel(module, annotation, arguments); + }; + + var refactoredCode = RefactoredCode(code, modelBuilder); + + Assert.AreEqual(expectedCode, refactoredCode); + } + + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoringAction_BooleanArgument_Undefined() + { + const string code = @" +Public Sub Foo() +End Sub +"; + const string expectedCode = @"'@ModuleAttribute VB_Exposed, NOT_A_BOOLEAN + +Public Sub Foo() +End Sub +"; + Func modelBuilder = (state) => + { + var module = state.DeclarationFinder + .UserDeclarations(DeclarationType.ProceduralModule) + .Single(); + var annotation = new ModuleAttributeAnnotation(); + var arguments = new List + { + new TypedAnnotationArgument(AnnotationArgumentType.Attribute, "VB_Exposed"), + new TypedAnnotationArgument(AnnotationArgumentType.Boolean, "aefefef") + }; + + return new AnnotateDeclarationModel(module, annotation, arguments); + }; + + var refactoredCode = RefactoredCode(code, modelBuilder); + + Assert.AreEqual(expectedCode, refactoredCode); + } + + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoringAction_NumberArgument() + { + const string code = @" +Public Sub Foo() +End Sub +"; + const string expectedCode = @" +'@MemberAttribute VB_UserMemId, 0 +Public Sub Foo() +End Sub +"; + Func modelBuilder = (state) => + { + var module = state.DeclarationFinder + .UserDeclarations(DeclarationType.Procedure) + .Single(); + var annotation = new MemberAttributeAnnotation(); + var arguments = new List + { + new TypedAnnotationArgument(AnnotationArgumentType.Attribute, "VB_UserMemId"), + new TypedAnnotationArgument(AnnotationArgumentType.Number, "0") + }; + + return new AnnotateDeclarationModel(module, annotation, arguments); + }; + + var refactoredCode = RefactoredCode(code, modelBuilder); + + Assert.AreEqual(expectedCode, refactoredCode); + } + + + protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) + { + var annotationUpdater = new AnnotationUpdater(); + return new AnnotateDeclarationRefactoringAction(rewritingManager, annotationUpdater); + } + } +} \ No newline at end of file diff --git a/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringTests.cs b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringTests.cs new file mode 100644 index 0000000000..906410abe8 --- /dev/null +++ b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringTests.cs @@ -0,0 +1,79 @@ +using System.Linq; +using NUnit.Framework; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.AnnotateDeclaration; +using Rubberduck.VBEditor.SafeComWrappers; +using Rubberduck.VBEditor.Utility; + +namespace RubberduckTests.Refactoring.AnnotateDeclaration +{ + [TestFixture] + public class AnnotateDeclarationRefactoringTests : InteractiveRefactoringTestBase + { + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoring_InitialModel_TargetIsPassedInTarget() + { + const string code = @" +Public Sub Foo() +End Sub +"; + var model = InitialModel( + "TestModule", + DeclarationType.ProceduralModule, + ("TestModule", code, ComponentType.StandardModule)); + + var targetName = model.Target.IdentifierName; + + Assert.AreEqual("TestModule", targetName); + } + + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoring_InitialModel_AnnotationNull() + { + const string code = @" +Public Sub Foo() +End Sub +"; + var model = InitialModel( + "TestModule", + DeclarationType.ProceduralModule, + ("TestModule", code, ComponentType.StandardModule)); + + Assert.IsNull(model.Annotation); + } + + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoring_InitialModel_ArgumentsEmpty() + { + const string code = @" +Public Sub Foo() +End Sub +"; + var model = InitialModel( + "TestModule", + DeclarationType.ProceduralModule, + ("TestModule", code, ComponentType.StandardModule)); + + Assert.IsFalse(model.Arguments.Any()); + } + + protected override IRefactoring TestRefactoring( + IRewritingManager rewritingManager, + RubberduckParserState state, + RefactoringUserInteraction userInteraction, + ISelectionService selectionService) + { + var annotationUpdater = new AnnotationUpdater(); + var annotateDeclarationAction = new AnnotateDeclarationRefactoringAction(rewritingManager, annotationUpdater); + + var selectedDeclarationProvider = new SelectedDeclarationProvider(selectionService, state); + return new AnnotateDeclarationRefactoring(annotateDeclarationAction, selectedDeclarationProvider, selectionService, userInteraction); + } + } +} \ No newline at end of file diff --git a/RubberduckTests/Refactoring/MoveToFolder/MoveToFolderRefactoringTests.cs b/RubberduckTests/Refactoring/MoveToFolder/MoveToFolderRefactoringTests.cs index 057a0cf878..8f58710de6 100644 --- a/RubberduckTests/Refactoring/MoveToFolder/MoveToFolderRefactoringTests.cs +++ b/RubberduckTests/Refactoring/MoveToFolder/MoveToFolderRefactoringTests.cs @@ -4,7 +4,6 @@ using NUnit.Framework; using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.UIContext; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; using Rubberduck.Refactorings.Exceptions; From e20d87038e87caf03bd24b003241c9a8016d6dc4 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Fri, 8 May 2020 07:59:02 -0700 Subject: [PATCH 14/82] Resolved issue Added MCVE tests. Corrected XMLDoc example. --- .../Concrete/ProcedureNotUsedInspection.cs | 17 +++++-- .../ProcedureNotUsedInspectionTests.cs | 51 +++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs index 884011b4ae..a43a5143ce 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs @@ -20,15 +20,23 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete /// /// Not all unused procedures can/should be removed: ignore any inspection results for /// event handler procedures and interface members that Rubberduck isn't recognizing as such. + /// Public procedures of Standard Modules are not flagged by this inspection regardless of + /// the presence or absence of user code references. /// /// /// /// /// /// @@ -36,9 +44,13 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete /// /// @@ -78,8 +90,7 @@ public ProcedureNotUsedInspection(IDeclarationFinderProvider declarationFinderPr protected override bool IsResultDeclaration(Declaration declaration, DeclarationFinder finder) { return !declaration.References - .Any(reference => !reference.IsAssignment - && !reference.ParentScoping.Equals(declaration)) // recursive calls don't count + .Any(reference => !reference.ParentScoping.Equals(declaration)) // recursive calls don't count && !finder.FindEventHandlers().Contains(declaration) && !IsPublicModuleMember(declaration) && !IsClassLifeCycleHandler(declaration) diff --git a/RubberduckTests/Inspections/ProcedureNotUsedInspectionTests.cs b/RubberduckTests/Inspections/ProcedureNotUsedInspectionTests.cs index b2c5c3f594..9b9c2afd8a 100644 --- a/RubberduckTests/Inspections/ProcedureNotUsedInspectionTests.cs +++ b/RubberduckTests/Inspections/ProcedureNotUsedInspectionTests.cs @@ -182,6 +182,57 @@ public void ProcedureNotUsed_Ignored_DoesNotReturnResult() Assert.AreEqual(0, InspectionResultsForStandardModule(inputCode).Count()); } + //https://github.com/rubberduck-vba/Rubberduck/issues/5490 + [TestCase(@"Name = ""Bizz""", 0)] + [TestCase(@"mName = ""Bizz""", 1)] + [Category("Inspections")] + public void PropertyLet(string assignmentCode, int expectedResults) + { + var inputCode = +$@" +Private mName As String + +Private Sub Class_Initialize() + {assignmentCode} +End Sub + +Private Property Let Name(ByVal value As String) + mName = value +End Property +"; + + var modules = new(string, string, ComponentType)[] + { + (MockVbeBuilder.TestModuleName, inputCode, ComponentType.ClassModule), + }; + + Assert.AreEqual(expectedResults, InspectionResultsForModules(modules).Count(result => result.Target.DeclarationType.HasFlag(DeclarationType.Procedure))); + } + + [Test] + [Category("Inspections")] + public void RecursiveReferenceOnly_ReturnsResult() + { + var inputCode = +$@" +Private mName As String + +Private Property Let Name(ByVal value As String) + mName = value + If Len(mName) > 10 Then + Name = Left(mName, 8) + End If +End Property +"; + + var modules = new(string, string, ComponentType)[] + { + (MockVbeBuilder.TestModuleName, inputCode, ComponentType.ClassModule), + }; + + Assert.AreEqual(1, InspectionResultsForModules(modules).Count(result => result.Target.DeclarationType.HasFlag(DeclarationType.Procedure))); + } + [Test] [Category("Inspections")] public void InspectionName() From 5f24969dfdd7ee701a50ad9b1328f1705ed3c1d8 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Mon, 27 Apr 2020 19:38:05 +0200 Subject: [PATCH 15/82] Add UI for AnnotateDeclarationRefactoring Also adds INotifyErrorInfo to ViewModelBase in order to allow more idiomatic validation in the UI. --- Rubberduck.Core/Rubberduck.Core.csproj | 3 + .../BoolToVisibleVisibilityConverter.cs | 4 - .../DeclarationToMemberSignatureConverter.cs | 1 - .../DeclarationToQualifiedNameConverter.cs | 40 +++ .../EnumToLocalizedStringConverter.cs | 40 +++ .../InspectionToLocalizedNameConverter.cs | 32 ++ .../AnnotateDeclarationPresenter.cs | 14 + .../AnnotateDeclarationView.xaml | 181 ++++++++++ .../AnnotateDeclarationView.xaml.cs | 13 + .../AnnotateDeclarationViewModel.cs | 152 ++++++++ ...ionArgumentTypeCellDataTemplateSelector.cs | 24 ++ ...ionArgumentValueCelDataTemplateSelector.cs | 28 ++ .../AnnotationArgumentViewModel.cs | 167 +++++++++ .../AnnotationArgumentViewModelFactory.cs | 26 ++ .../IAnnotationArgumentViewModelFactory.cs | 9 + Rubberduck.Core/UI/ViewModelBase.cs | 91 ++++- .../Root/RubberduckIoCInstaller.cs | 4 + .../Annotations/AnnotationBase.cs | 17 + .../FixedAttributeValueAnnotationBase.cs | 2 +- .../FlexibleAttributeValueAnnotationBase.cs | 2 +- Rubberduck.Parsing/Annotations/IAnnotation.cs | 2 +- .../AnnotateDeclarationModel.cs | 2 +- Rubberduck.Resources/RubberduckUI.Designer.cs | 216 +++++++++++ Rubberduck.Resources/RubberduckUI.de.resx | 72 ++++ Rubberduck.Resources/RubberduckUI.resx | 73 ++++ .../AnnotateDeclarationViewModelTests.cs | 340 ++++++++++++++++++ .../AnnotationArgumentViewModelTests.cs | 233 ++++++++++++ 27 files changed, 1777 insertions(+), 11 deletions(-) create mode 100644 Rubberduck.Core/UI/Converters/DeclarationToQualifiedNameConverter.cs create mode 100644 Rubberduck.Core/UI/Converters/EnumToLocalizedStringConverter.cs create mode 100644 Rubberduck.Core/UI/Converters/InspectionToLocalizedNameConverter.cs create mode 100644 Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationPresenter.cs create mode 100644 Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml create mode 100644 Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml.cs create mode 100644 Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationViewModel.cs create mode 100644 Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentTypeCellDataTemplateSelector.cs create mode 100644 Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentValueCelDataTemplateSelector.cs create mode 100644 Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModel.cs create mode 100644 Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModelFactory.cs create mode 100644 Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/IAnnotationArgumentViewModelFactory.cs create mode 100644 RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationViewModelTests.cs create mode 100644 RubberduckTests/Refactoring/AnnotateDeclaration/AnnotationArgumentViewModelTests.cs diff --git a/Rubberduck.Core/Rubberduck.Core.csproj b/Rubberduck.Core/Rubberduck.Core.csproj index 4078ea58d7..a769075f30 100644 --- a/Rubberduck.Core/Rubberduck.Core.csproj +++ b/Rubberduck.Core/Rubberduck.Core.csproj @@ -117,6 +117,9 @@ True True + + AnnotateDeclarationView.xaml + MoveMultipleFoldersView.xaml diff --git a/Rubberduck.Core/UI/Converters/BoolToVisibleVisibilityConverter.cs b/Rubberduck.Core/UI/Converters/BoolToVisibleVisibilityConverter.cs index 11cbfff5ad..bf58dd2368 100644 --- a/Rubberduck.Core/UI/Converters/BoolToVisibleVisibilityConverter.cs +++ b/Rubberduck.Core/UI/Converters/BoolToVisibleVisibilityConverter.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Data; diff --git a/Rubberduck.Core/UI/Converters/DeclarationToMemberSignatureConverter.cs b/Rubberduck.Core/UI/Converters/DeclarationToMemberSignatureConverter.cs index b977fc61c1..c7918f8ed0 100644 --- a/Rubberduck.Core/UI/Converters/DeclarationToMemberSignatureConverter.cs +++ b/Rubberduck.Core/UI/Converters/DeclarationToMemberSignatureConverter.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Windows; using System.Windows.Data; namespace Rubberduck.UI.Converters diff --git a/Rubberduck.Core/UI/Converters/DeclarationToQualifiedNameConverter.cs b/Rubberduck.Core/UI/Converters/DeclarationToQualifiedNameConverter.cs new file mode 100644 index 0000000000..fbba11097b --- /dev/null +++ b/Rubberduck.Core/UI/Converters/DeclarationToQualifiedNameConverter.cs @@ -0,0 +1,40 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; +using Rubberduck.Parsing.Symbols; + +namespace Rubberduck.UI.Converters +{ + public class DeclarationToQualifiedNameConverter : IValueConverter + { + private readonly IValueConverter _declarationTypeConverter; + + public DeclarationToQualifiedNameConverter() + { + _declarationTypeConverter = new EnumToLocalizedStringConverter + { + ResourcePrefix = "DeclarationType_" + }; + + } + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (!(value is Declaration declaration)) + { + throw new ArgumentException("The value must be an instance of Declaration.", "value"); + } + + var qualifiedNameText = declaration.QualifiedName.ToString(); + var declarationTypeText = _declarationTypeConverter.Convert(declaration.DeclarationType, targetType, null, culture); + + return $"{qualifiedNameText} ({declarationTypeText})"; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return DependencyProperty.UnsetValue; + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Converters/EnumToLocalizedStringConverter.cs b/Rubberduck.Core/UI/Converters/EnumToLocalizedStringConverter.cs new file mode 100644 index 0000000000..ad012167ac --- /dev/null +++ b/Rubberduck.Core/UI/Converters/EnumToLocalizedStringConverter.cs @@ -0,0 +1,40 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using Rubberduck.Resources; + +namespace Rubberduck.UI.Converters +{ + //Based on https://stackoverflow.com/a/29659265 by Yoh Deadfall + public class EnumToLocalizedStringConverter : IValueConverter + { + public string ResourcePrefix { get; set; } + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + { + throw new ArgumentException("The value cannot be null.", "value"); + } + + //TODO: Make this independent of the resource used. + return RubberduckUI.ResourceManager.GetString(ResourcePrefix + value.ToString(), culture); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + var str = (string)value; + + foreach (var enumValue in Enum.GetValues(targetType)) + { + //TODO: Make this independent of the resource used. + if (str == RubberduckUI.ResourceManager.GetString(ResourcePrefix + enumValue.ToString(), culture)) + { + return enumValue; + } + } + + throw new ArgumentException("There is no enumeration member corresponding to the specified name.", "value"); + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Converters/InspectionToLocalizedNameConverter.cs b/Rubberduck.Core/UI/Converters/InspectionToLocalizedNameConverter.cs new file mode 100644 index 0000000000..209060001d --- /dev/null +++ b/Rubberduck.Core/UI/Converters/InspectionToLocalizedNameConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; +using Rubberduck.CodeAnalysis.Inspections; +using Rubberduck.Resources.Inspections; + +namespace Rubberduck.UI.Converters +{ + public class InspectionToLocalizedNameConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var inspectionName = value is IInspection inspection + ? inspection.Name + : value as string; + + if (inspectionName == null) + { + + throw new ArgumentException("The value must be an instance of IInspection or a string containing the programmatic name of an inspection.", "value"); + } + + return InspectionNames.ResourceManager.GetString(inspectionName, culture); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return DependencyProperty.UnsetValue; + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationPresenter.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationPresenter.cs new file mode 100644 index 0000000000..98982a105a --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationPresenter.cs @@ -0,0 +1,14 @@ +using Rubberduck.Refactorings.AnnotateDeclaration; +using Rubberduck.Resources; + +namespace Rubberduck.UI.Refactorings.AnnotateDeclaration +{ + internal class AnnotateDeclarationPresenter : RefactoringPresenterBase, IAnnotateDeclarationPresenter + { + private static readonly DialogData DialogData = DialogData.Create(RubberduckUI.AnnotateDeclarationDialog_Caption, 164, 684); + + public AnnotateDeclarationPresenter(AnnotateDeclarationModel model, IRefactoringDialogFactory dialogFactory) : + base(DialogData, model, dialogFactory) + {} + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml new file mode 100644 index 0000000000..dfffb2b429 --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml.cs new file mode 100644 index 0000000000..b32636769a --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml.cs @@ -0,0 +1,13 @@ +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.AnnotateDeclaration; + +namespace Rubberduck.UI.Refactorings.AnnotateDeclaration +{ + public partial class AnnotateDeclarationView : IRefactoringView + { + public AnnotateDeclarationView() + { + InitializeComponent(); + } + } +} diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationViewModel.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationViewModel.cs new file mode 100644 index 0000000000..ada8d28394 --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationViewModel.cs @@ -0,0 +1,152 @@ +using System.Collections.Generic; +using System.Linq; +using NLog; +using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Refactorings.AnnotateDeclaration; +using Rubberduck.UI.Command; +using Rubberduck.UI.WPF; + +namespace Rubberduck.UI.Refactorings.AnnotateDeclaration +{ + internal class AnnotateDeclarationViewModel : RefactoringViewModelBase + { + private readonly IAnnotationArgumentViewModelFactory _argumentFactory; + + public AnnotateDeclarationViewModel( + AnnotateDeclarationModel model, + IEnumerable annotations, + IAnnotationArgumentViewModelFactory argumentFactory + ) + : base(model) + { + _argumentFactory = argumentFactory; + ApplicableAnnotations = AnnotationsForDeclaration(model.Target, annotations); + AnnotationArguments = new ObservableViewModelCollection(); + RefreshAnnotationArguments(Model.Annotation); + + AddAnnotationArgument = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteAddArgument, parameter => CanAddArgument); + RemoveAnnotationArgument = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteRemoveArgument, parameter => CanRemoveArgument); + } + + public IReadOnlyList ApplicableAnnotations { get; } + + private static IReadOnlyList AnnotationsForDeclaration(Declaration declaration, IEnumerable annotations) + { + return AnnotationsForDeclarationType(declaration.DeclarationType, annotations) + .Where(annotation => annotation.AllowMultiple + || !declaration.Annotations.Any(pta => annotation.Equals(pta.Annotation))) + .ToList(); + } + + private static IEnumerable AnnotationsForDeclarationType(DeclarationType declarationType, IEnumerable annotations) + { + if (declarationType.HasFlag(DeclarationType.Module)) + { + return annotations.Where(annotation => annotation.Target.HasFlag(AnnotationTarget.Module)); + } + + if (declarationType.HasFlag(DeclarationType.Member) + && declarationType != DeclarationType.LibraryProcedure + && declarationType != DeclarationType.LibraryFunction) + { + return annotations.Where(annotation => annotation.Target.HasFlag(AnnotationTarget.Member)); + } + + if (declarationType.HasFlag(DeclarationType.Variable)) + { + return annotations.Where(annotation => annotation.Target.HasFlag(AnnotationTarget.Variable)); + } + + return Enumerable.Empty(); + } + + public IAnnotation Annotation + { + get => Model.Annotation; + set + { + if (value == null && Model.Annotation == null || (value?.Equals(Model.Annotation) ?? false)) + { + return; + } + + Model.Annotation = value; + RefreshAnnotationArguments(value); + + OnPropertyChanged(); + } + } + + public ObservableViewModelCollection AnnotationArguments { get; } + + private void RefreshAnnotationArguments(IAnnotation annotation) + { + AnnotationArguments.Clear(); + var newArguments = InitialAnnotationArguments(annotation); + + foreach (var argument in newArguments) + { + AnnotationArguments.Add(argument); + } + } + + private IEnumerable InitialAnnotationArguments(IAnnotation annotation) + { + return annotation != null + ? annotation.AllowedArgumentTypes + .Select(InitialArgumentViewModel) + .Take(annotation.RequiredArguments) + : Enumerable.Empty(); + } + + private IAnnotationArgumentViewModel InitialArgumentViewModel(AnnotationArgumentType argumentType) + { + return _argumentFactory.Create(argumentType, string.Empty); + } + + public CommandBase AddAnnotationArgument { get; } + public CommandBase RemoveAnnotationArgument { get; } + + private bool CanAddArgument => Annotation != null + && (!Annotation.AllowedArguments.HasValue + || AnnotationArguments.Count < Annotation.AllowedArguments.Value); + + private bool CanRemoveArgument => Annotation != null + && AnnotationArguments.Count > Model.Annotation.RequiredArguments; + + private void ExecuteAddArgument(object parameter) + { + if (Annotation == null) + { + return; + } + + var argumentType = Annotation.AllowedArgumentTypes.Last(); + var newArgument = InitialArgumentViewModel(argumentType); + AnnotationArguments.Add(newArgument); + } + + private void ExecuteRemoveArgument(object parameter) + { + if (!AnnotationArguments.Any()) + { + return; + } + + AnnotationArguments.RemoveAt(AnnotationArguments.Count - 1); + } + + protected override void DialogOk() + { + Model.Arguments = AnnotationArguments + .Select(viewModel => viewModel.Model) + .ToList(); + + base.DialogOk(); + } + + public bool IsValidAnnotation => Annotation != null + && AnnotationArguments.All(argument => !argument.HasErrors); + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentTypeCellDataTemplateSelector.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentTypeCellDataTemplateSelector.cs new file mode 100644 index 0000000000..0fb2d58d52 --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentTypeCellDataTemplateSelector.cs @@ -0,0 +1,24 @@ +using System.Windows; +using System.Windows.Controls; + +namespace Rubberduck.UI.Refactorings.AnnotateDeclaration +{ + public class AnnotationArgumentTypeCellDataTemplateSelector : DataTemplateSelector + { + public override DataTemplate SelectTemplate(object item, DependencyObject container) + { + if (!(container is FrameworkElement element) + || !(item is AnnotationArgumentViewModel typedArgument)) + { + return null; + } + + if (typedArgument.CanEditArgumentType) + { + return element.FindResource("ArgumentMultiTypeTemplate") as DataTemplate; + } + + return element.FindResource("ArgumentSingleTypeTemplate") as DataTemplate; + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentValueCelDataTemplateSelector.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentValueCelDataTemplateSelector.cs new file mode 100644 index 0000000000..af846df564 --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentValueCelDataTemplateSelector.cs @@ -0,0 +1,28 @@ +using System.Windows; +using System.Windows.Controls; +using Rubberduck.Parsing.Annotations; + +namespace Rubberduck.UI.Refactorings.AnnotateDeclaration +{ + public class AnnotationArgumentValueCellDataTemplateSelector : DataTemplateSelector + { + public override DataTemplate SelectTemplate(object item, DependencyObject container) + { + if (!(container is FrameworkElement element) + || !(item is AnnotationArgumentViewModel typedArgument)) + { + return null; + } + + switch (typedArgument.ArgumentType) + { + case AnnotationArgumentType.Inspection: + return element.FindResource("ArgumentValueInspectionTemplate") as DataTemplate; + case AnnotationArgumentType.Boolean: + return element.FindResource("ArgumentValueBooleanTemplate") as DataTemplate; + default: + return element.FindResource("ArgumentValueTemplate") as DataTemplate; + } + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModel.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModel.cs new file mode 100644 index 0000000000..55af64e9d4 --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModel.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using Rubberduck.Parsing.Annotations; +using Rubberduck.Refactorings.AnnotateDeclaration; +using Rubberduck.Resources; + +namespace Rubberduck.UI.Refactorings.AnnotateDeclaration +{ + internal interface IAnnotationArgumentViewModel : INotifyPropertyChanged, INotifyDataErrorInfo + { + TypedAnnotationArgument Model { get; } + IReadOnlyList ApplicableArgumentTypes { get; } + IReadOnlyList InspectionNames { get; } + bool CanEditArgumentType { get; } + AnnotationArgumentType ArgumentType { get; set; } + string ArgumentValue { get; set; } + } + + internal class AnnotationArgumentViewModel : ViewModelBase, IAnnotationArgumentViewModel + { + private const int MaxAllowedCharacters = 511; + + private TypedAnnotationArgument _model; + + public AnnotationArgumentViewModel(TypedAnnotationArgument model, IReadOnlyList inspectionNames) + { + _model = model; + ApplicableArgumentTypes = ApplicableTypes(_model.ArgumentType); + _model.ArgumentType = ApplicableArgumentTypes.FirstOrDefault(); + InspectionNames = inspectionNames; + ValidateArgument(); + } + + private IReadOnlyList ApplicableTypes(AnnotationArgumentType argumentType) + { + return Enum.GetValues(typeof(AnnotationArgumentType)) + .OfType() + .Where(t => argumentType.HasFlag(t)) + .ToList(); + } + + public TypedAnnotationArgument Model => _model; + public IReadOnlyList ApplicableArgumentTypes { get; } + + public bool CanEditArgumentType => ApplicableArgumentTypes.Count > 1; + public IReadOnlyList InspectionNames { get; } + + public AnnotationArgumentType ArgumentType + { + get => _model.ArgumentType; + set + { + if (_model.ArgumentType == value) + { + return; + } + + _model.ArgumentType = value; + ValidateArgument(); + OnPropertyChanged(); + } + } + + public string ArgumentValue + { + get => _model.Argument; + set + { + if (_model.Argument == value) + { + return; + } + + _model.Argument = value; + ValidateArgument(); + OnPropertyChanged(); + } + } + + private void ValidateArgument() + { + var errors = ArgumentValidationErrors(); + + if (errors.Any()) + { + SetErrors("SelectedArgumentType", errors); + } + else + { + ClearErrors(); + } + } + + private List ArgumentValidationErrors() + { + var errors = new List(); + + if (string.IsNullOrEmpty(ArgumentValue)) + { + errors.Add(RubberduckUI.AnnotationArgument_ValidationError_EmptyArgument); + } + if (ArgumentValue.Length > MaxAllowedCharacters) + { + errors.Add(string.Format(RubberduckUI.AnnotationArgument_ValidationError_TooLong, MaxAllowedCharacters)); + } + if (ContainsNewline(ArgumentValue)) + { + errors.Add(RubberduckUI.AnnotationArgument_ValidationError_Newline); + } + else if (ContainsControlCharacter(ArgumentValue)) + { + errors.Add(RubberduckUI.AnnotationArgument_ValidationError_SpecialCharacters); + } + + switch (ArgumentType) + { + case AnnotationArgumentType.Attribute: + if (!ArgumentValue.StartsWith("VB_") || ContainsWhitespace(ArgumentValue)) + { + errors.Add(RubberduckUI.AnnotationArgument_ValidationError_AttributeNameStart); + } + if (ContainsWhitespace(ArgumentValue)) + { + errors.Add(RubberduckUI.AnnotationArgument_ValidationError_WhitespaceInAttribute); + } + break; + case AnnotationArgumentType.Inspection: + if (!InspectionNames.Contains(ArgumentValue)) + { + errors.Add(RubberduckUI.AnnotationArgument_ValidationError_InspectionName); + } + break; + case AnnotationArgumentType.Boolean: + if (!bool.TryParse(ArgumentValue, out _)) + { + errors.Add(RubberduckUI.AnnotationArgument_ValidationError_NotABoolean); + } + break; + case AnnotationArgumentType.Number: + if (!decimal.TryParse(ArgumentValue, out _)) + { + errors.Add(RubberduckUI.AnnotationArgument_ValidationError_NotANumber); + } + break; + } + + return errors; + } + + private bool ContainsNewline(string argumentText) + { + return argumentText.Contains('\n') || argumentText.Contains('\r'); + } + + private bool ContainsControlCharacter(string argumentText) + { + return argumentText.Any(char.IsControl); + } + + private bool ContainsWhitespace(string argumentText) + { + return argumentText.Any(char.IsWhiteSpace); + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModelFactory.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModelFactory.cs new file mode 100644 index 0000000000..e3730a477d --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModelFactory.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Linq; +using Rubberduck.CodeAnalysis.Inspections; +using Rubberduck.Parsing.Annotations; +using Rubberduck.Refactorings.AnnotateDeclaration; + +namespace Rubberduck.UI.Refactorings.AnnotateDeclaration +{ + internal class AnnotationArgumentViewModelFactory : IAnnotationArgumentViewModelFactory + { + private readonly IReadOnlyList InspectionNames; + + public AnnotationArgumentViewModelFactory(IEnumerable inspections) + { + InspectionNames = inspections + .Select(inspection => inspection.AnnotationName) + .ToList(); + } + + public IAnnotationArgumentViewModel Create(AnnotationArgumentType argumentType, string argument = null) + { + var model = new TypedAnnotationArgument(argumentType, argument ?? string.Empty); + return new AnnotationArgumentViewModel(model, InspectionNames); + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/IAnnotationArgumentViewModelFactory.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/IAnnotationArgumentViewModelFactory.cs new file mode 100644 index 0000000000..3a65d314ff --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/IAnnotationArgumentViewModelFactory.cs @@ -0,0 +1,9 @@ +using Rubberduck.Parsing.Annotations; + +namespace Rubberduck.UI.Refactorings.AnnotateDeclaration +{ + internal interface IAnnotationArgumentViewModelFactory + { + IAnnotationArgumentViewModel Create(AnnotationArgumentType argumentType, string argument = null); + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/ViewModelBase.cs b/Rubberduck.Core/UI/ViewModelBase.cs index 54cb3f3701..c60da71a3a 100644 --- a/Rubberduck.Core/UI/ViewModelBase.cs +++ b/Rubberduck.Core/UI/ViewModelBase.cs @@ -1,16 +1,23 @@ -using System.ComponentModel; +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using System.IO; +using System.Linq; using System.Runtime.CompilerServices; using System.Windows.Media.Imaging; using Rubberduck.Properties; namespace Rubberduck.UI { - public abstract class ViewModelBase : INotifyPropertyChanged + public abstract class ViewModelBase : INotifyPropertyChanged, INotifyDataErrorInfo { public event PropertyChangedEventHandler PropertyChanged; + public event EventHandler ErrorsChanged; + + private readonly IDictionary> _errors = new Dictionary>(); [NotifyPropertyChangedInvocator] protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) @@ -34,5 +41,85 @@ protected static BitmapImage GetImageSource(Bitmap image) return bitmapImage; } } + + protected virtual void OnErrorsChanged(string propertyName = null) + { + ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); + } + + public IEnumerable GetErrors(string propertyName) + { + return _errors.TryGetValue(propertyName, out var errorList) + ? errorList + : null; + } + + public bool HasErrors => _errors.Any(); + + /// + /// Replaces all errors for a property and notifies all consumers. + /// + /// Name of the property + /// List of texts describing each error + protected void SetErrors(string propertyName, List errorTexts) + { + if (propertyName == null) + { + return; + } + + _errors[propertyName] = errorTexts; + OnErrorsChanged(propertyName); + } + + /// + /// Adds a single error for a property and notifies all consumers. + /// + /// Name of the property + /// Text describing the error + protected void AddError(string propertyName, string errorText) + { + if (propertyName == null) + { + return; + } + + if (_errors.TryGetValue(propertyName, out var errorList)) + { + errorList.Add(errorText); + } + else + { + _errors.Add(propertyName, new List{errorText}); + } + + OnErrorsChanged(propertyName); + } + + /// + /// Clears all errors for a property and notifies all consumers. + /// + /// If no argument or null is provided, all errors will be cleared. + /// + /// + /// Name of the property + protected void ClearErrors(string propertyName = null) + { + if (!_errors.Any()) + { + return; + } + + if (propertyName == null) + { + _errors.Clear(); + OnErrorsChanged(); + } + else if (_errors.ContainsKey(propertyName)) + { + _errors.Remove(propertyName); + OnErrorsChanged(propertyName); + } + } } } diff --git a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs index 88b889204f..1c706459cb 100644 --- a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs +++ b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs @@ -60,6 +60,7 @@ using Rubberduck.VBEditor.SourceCodeHandling; using Rubberduck.VBEditor.VbeRuntime; using Rubberduck.Parsing.Annotations; +using Rubberduck.UI.Refactorings.AnnotateDeclaration; namespace Rubberduck.Root { @@ -376,6 +377,9 @@ private void RegisterSpecialFactories(IWindsorContainer container) container.Register(Component.For() .ImplementedBy() .LifestyleSingleton()); + container.Register(Component.For() + .ImplementedBy() + .LifestyleSingleton()); RegisterUnreachableCaseFactories(container); } diff --git a/Rubberduck.Parsing/Annotations/AnnotationBase.cs b/Rubberduck.Parsing/Annotations/AnnotationBase.cs index 6e598c42a4..a535899b90 100644 --- a/Rubberduck.Parsing/Annotations/AnnotationBase.cs +++ b/Rubberduck.Parsing/Annotations/AnnotationBase.cs @@ -26,5 +26,22 @@ public virtual IReadOnlyList ProcessAnnotationArguments(IEnumerable AttributeToAnnotationValues(IReadOnlyList a public bool MatchesAttributeDefinition(string attributeName, IReadOnlyList attributeValues) { - return _attribute == attributeName && this._attributeValues.SequenceEqual(attributeValues); + return _attribute == attributeName && _attributeValues.SequenceEqual(attributeValues); } } } \ No newline at end of file diff --git a/Rubberduck.Parsing/Annotations/Concrete/FlexibleAttributeValueAnnotationBase.cs b/Rubberduck.Parsing/Annotations/Concrete/FlexibleAttributeValueAnnotationBase.cs index c68207e23c..32c966c519 100644 --- a/Rubberduck.Parsing/Annotations/Concrete/FlexibleAttributeValueAnnotationBase.cs +++ b/Rubberduck.Parsing/Annotations/Concrete/FlexibleAttributeValueAnnotationBase.cs @@ -10,7 +10,7 @@ public abstract class FlexibleAttributeValueAnnotationBase : AnnotationBase, IAt private readonly int _numberOfValues; protected FlexibleAttributeValueAnnotationBase(string name, AnnotationTarget target, string attribute, int numberOfValues, IReadOnlyList argumentTypes) - : base(name, target, numberOfValues, numberOfValues) + : base(name, target, numberOfValues, numberOfValues, argumentTypes) { _attribute = attribute; _numberOfValues = numberOfValues; diff --git a/Rubberduck.Parsing/Annotations/IAnnotation.cs b/Rubberduck.Parsing/Annotations/IAnnotation.cs index b7a2003d2b..3608c4ec4f 100644 --- a/Rubberduck.Parsing/Annotations/IAnnotation.cs +++ b/Rubberduck.Parsing/Annotations/IAnnotation.cs @@ -66,7 +66,7 @@ public enum AnnotationArgumentType /// /// Indicates that the annotation argument can be a string. /// - Text = 1 << 0, + Text = 1, /// /// Indicates that the annotation argument can be a number. /// diff --git a/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationModel.cs b/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationModel.cs index 7af819c312..c1c5f35989 100644 --- a/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationModel.cs +++ b/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationModel.cs @@ -21,7 +21,7 @@ public class AnnotateDeclarationModel : IRefactoringModel { public Declaration Target { get; } public IAnnotation Annotation { get; set; } - public IList Arguments { get; } + public IList Arguments { get; set; } public AnnotateDeclarationModel( Declaration target, diff --git a/Rubberduck.Resources/RubberduckUI.Designer.cs b/Rubberduck.Resources/RubberduckUI.Designer.cs index acfc8de57f..0b1eba6bcd 100644 --- a/Rubberduck.Resources/RubberduckUI.Designer.cs +++ b/Rubberduck.Resources/RubberduckUI.Designer.cs @@ -153,6 +153,222 @@ public class RubberduckUI { } } + /// + /// Looks up a localized string similar to Add. + /// + public static string AnnotateDeclarationDialog_AddArgumentButtonLabel { + get { + return ResourceManager.GetString("AnnotateDeclarationDialog_AddArgumentButtonLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Annotation to Add:. + /// + public static string AnnotateDeclarationDialog_AnnotationLabel { + get { + return ResourceManager.GetString("AnnotateDeclarationDialog_AnnotationLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Annotation Arguments:. + /// + public static string AnnotateDeclarationDialog_ArgumentsLabel { + get { + return ResourceManager.GetString("AnnotateDeclarationDialog_ArgumentsLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + public static string AnnotateDeclarationDialog_ArgumentTypeHeader { + get { + return ResourceManager.GetString("AnnotateDeclarationDialog_ArgumentTypeHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Value. + /// + public static string AnnotateDeclarationDialog_ArgumentValueHeader { + get { + return ResourceManager.GetString("AnnotateDeclarationDialog_ArgumentValueHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rubberduck - Annotate Declaration. + /// + public static string AnnotateDeclarationDialog_Caption { + get { + return ResourceManager.GetString("AnnotateDeclarationDialog_Caption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Declaration to Annotate:. + /// + public static string AnnotateDeclarationDialog_DeclarationLabel { + get { + return ResourceManager.GetString("AnnotateDeclarationDialog_DeclarationLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select an annotation (and values) to add to the selected declaration.. + /// + public static string AnnotateDeclarationDialog_Instructions { + get { + return ResourceManager.GetString("AnnotateDeclarationDialog_Instructions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove. + /// + public static string AnnotateDeclarationDialog_RemoveArgumentButtonLabel { + get { + return ResourceManager.GetString("AnnotateDeclarationDialog_RemoveArgumentButtonLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Annotate Declaration. + /// + public static string AnnotateDeclarationDialog_TitleText { + get { + return ResourceManager.GetString("AnnotateDeclarationDialog_TitleText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Annotation arguments of type 'Attribute Name' have to start with 'VB_'.. + /// + public static string AnnotationArgument_ValidationError_AttributeNameStart { + get { + return ResourceManager.GetString("AnnotationArgument_ValidationError_AttributeNameStart", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Annotation arguments cannot be empty.. + /// + public static string AnnotationArgument_ValidationError_EmptyArgument { + get { + return ResourceManager.GetString("AnnotationArgument_ValidationError_EmptyArgument", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Annotation arguments of type 'Inspection Name' have to the name of an existing inspection.. + /// + public static string AnnotationArgument_ValidationError_InspectionName { + get { + return ResourceManager.GetString("AnnotationArgument_ValidationError_InspectionName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Annotation arguments can only contain a single line.. + /// + public static string AnnotationArgument_ValidationError_Newline { + get { + return ResourceManager.GetString("AnnotationArgument_ValidationError_Newline", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Annotation arguments of type 'Boolean' have to either be 'True' or 'False'.. + /// + public static string AnnotationArgument_ValidationError_NotABoolean { + get { + return ResourceManager.GetString("AnnotationArgument_ValidationError_NotABoolean", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Annotation arguments of type 'Number' have to be a valid number in integer of floating point format.. + /// + public static string AnnotationArgument_ValidationError_NotANumber { + get { + return ResourceManager.GetString("AnnotationArgument_ValidationError_NotANumber", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Annotation arguments must not contain control characters.. + /// + public static string AnnotationArgument_ValidationError_SpecialCharacters { + get { + return ResourceManager.GetString("AnnotationArgument_ValidationError_SpecialCharacters", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Annotation arguments cannot be longer than {0} characters.. + /// + public static string AnnotationArgument_ValidationError_TooLong { + get { + return ResourceManager.GetString("AnnotationArgument_ValidationError_TooLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Annotation arguments of type 'Attribute Name' cannot contain whitespace.. + /// + public static string AnnotationArgument_ValidationError_WhitespaceInAttribute { + get { + return ResourceManager.GetString("AnnotationArgument_ValidationError_WhitespaceInAttribute", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Attribute. + /// + public static string AnnotationArgumentType_Attribute { + get { + return ResourceManager.GetString("AnnotationArgumentType_Attribute", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Boolean. + /// + public static string AnnotationArgumentType_Boolean { + get { + return ResourceManager.GetString("AnnotationArgumentType_Boolean", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Inspection Name. + /// + public static string AnnotationArgumentType_Inspection { + get { + return ResourceManager.GetString("AnnotationArgumentType_Inspection", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Number. + /// + public static string AnnotationArgumentType_Number { + get { + return ResourceManager.GetString("AnnotationArgumentType_Number", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Text. + /// + public static string AnnotationArgumentType_Text { + get { + return ResourceManager.GetString("AnnotationArgumentType_Text", resourceCulture); + } + } + /// /// Looks up a localized string similar to Apply. /// diff --git a/Rubberduck.Resources/RubberduckUI.de.resx b/Rubberduck.Resources/RubberduckUI.de.resx index 642c4c443a..bf4683a27f 100644 --- a/Rubberduck.Resources/RubberduckUI.de.resx +++ b/Rubberduck.Resources/RubberduckUI.de.resx @@ -1607,4 +1607,76 @@ Import abgebrochen. Die Komponente '{0}' würde durch das Refactoring verändert, aber ihr Inhalt ist in Rubberduck veraltet. Bitte lassen Sie Rubberduck seinen Stand aktualisieren und versuchen Sie es erneut. + + Rubberduck - Annotation hinzufügen + + + Annotation hinzufügen + + + Annotationsarguments: + + + Hinzuzufügende Annotation: + + + Deklaration, zu welcher eine Annotation hinzugefügt werden soll: + + + Wert + + + Typ + + + Entfernen + + + Hinzufügen + + + Wähle eine Annotation (und Werte), die zu der ausgewählten Deklaration hinzugefügt werden soll. + + + Annotationsargumente dürfen keine Kontrollzeichen enthalten. + + + Annotationsargumente vom Typ 'Inspektionsname' müssen der Name einer existierenden Inspektion sein. + + + Annotationsargumente vom Typ 'Attributname' müssen mit 'VB_' anfangen. + + + Annotationsargumente vom Typ 'Zahl' müssen eine ganze Zahl oder eine Gleitkommazahl sein. + + + Annotationsargumente vom Typ 'Wahrheitswert' müssen entweder 'True' oder 'False' sein. + + + Annotationsargumente können nicht leer sein. + + + Text + + + Zahl + + + Inspektionsname + + + Wahrheitswert + + + Attribut + + + Annotationsargumente können nur aus einer einzigen Zeile bestehen. + + + Annotationsargumente vom Typ 'Attributname' dürfen keine Leerzeichen enthalten. + + + Annotationsargumente dürfen nicht länger als {0} Zeihen sein. + \ No newline at end of file diff --git a/Rubberduck.Resources/RubberduckUI.resx b/Rubberduck.Resources/RubberduckUI.resx index e21b8f3a73..38a1907c40 100644 --- a/Rubberduck.Resources/RubberduckUI.resx +++ b/Rubberduck.Resources/RubberduckUI.resx @@ -1822,4 +1822,77 @@ Import aborted. 'TODO implement interface member + + Rubberduck - Annotate Declaration + + + Annotate Declaration + + + Declaration to Annotate: + + + Annotation to Add: + + + Annotation Arguments: + + + Type + + + Value + + + Add + + + Remove + + + Select an annotation (and values) to add to the selected declaration. + + + Annotation arguments must not contain control characters. + + + Annotation arguments of type 'Inspection Name' have to the name of an existing inspection. + + + Annotation arguments of type 'Attribute Name' have to start with 'VB_'. + + + Annotation arguments of type 'Number' have to be a valid number in integer of floating point format. + + + Annotation arguments of type 'Boolean' have to either be 'True' or 'False'. + + + Annotation arguments cannot be empty. + + + Text + + + Number + + + Inspection Name + + + Boolean + + + Attribute + + + Annotation arguments can only contain a single line. + + + Annotation arguments of type 'Attribute Name' cannot contain whitespace. + + + Annotation arguments cannot be longer than {0} characters. + {0} max allowed characters + \ No newline at end of file diff --git a/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationViewModelTests.cs b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationViewModelTests.cs new file mode 100644 index 0000000000..c92f8333fa --- /dev/null +++ b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationViewModelTests.cs @@ -0,0 +1,340 @@ +using System.Collections.Generic; +using System.Linq; +using Moq; +using NUnit.Framework; +using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Refactorings.AnnotateDeclaration; +using Rubberduck.UI.Refactorings.AnnotateDeclaration; +using Rubberduck.VBEditor.SafeComWrappers; +using RubberduckTests.Mocks; + +namespace RubberduckTests.Refactoring.AnnotateDeclaration +{ + [TestFixture] + public class AnnotateDeclarationViewModelTests + { + [Test] + [TestCase(DeclarationType.Module, 4, "Exposed")] + [TestCase(DeclarationType.Procedure, 5, "DefaultMember")] + [TestCase(DeclarationType.Variable, 3, "VariableDescription")] + public void ApplicableAnnotationsAreFilteredBasedOnDeclarationType( + DeclarationType declarationType, + int expectedNumberOfAnnotations, + string expectedContainedAnnotation) + { + var viewModel = TestViewModel(declarationType); + var applicableAnnotationNames = viewModel.ApplicableAnnotations + .Select(annotation => annotation.Name) + .ToList(); + + Assert.AreEqual(expectedNumberOfAnnotations, applicableAnnotationNames.Count); + Assert.Contains(expectedContainedAnnotation, applicableAnnotationNames); + } + + [Test] + public void AnnotationAlreadyPresent_DoesNotAllowMultiple_NotInApplicableAnnotations() + { + var viewModel = TestViewModel(DeclarationType.Function); + var applicableAnnotationNames = viewModel.ApplicableAnnotations + .Select(annotation => annotation.Name) + .ToList(); + + Assert.False(applicableAnnotationNames.Contains("DefaultMember")); + } + + [Test] + public void AnnotationAlreadyPresent_AllowsMultiple_InApplicableAnnotations() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + var applicableAnnotationNames = viewModel.ApplicableAnnotations + .Select(annotation => annotation.Name) + .ToList(); + + Assert.True(applicableAnnotationNames.Contains("Ignore")); + } + + [Test] + public void AnnotationNull_Invalid() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = null; + + Assert.IsFalse(viewModel.IsValidAnnotation); + } + + [Test] + public void AnnotationNotNull_WithoutRequiredArguments_Valid() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = new TestMethodAnnotation(); + + Assert.IsTrue(viewModel.IsValidAnnotation); + } + + [Test] + public void AnnotationNotNull_WithArgumentsWithError_Valid() + { + var mockArgumentFactory = MockArgumentFactory(new List { true }); + + var viewModel = TestViewModel(DeclarationType.Procedure, mockArgumentFactory.Object); + viewModel.Annotation = new TestMethodAnnotation(); + viewModel.AddAnnotationArgument.Execute(null); + + Assert.IsFalse(viewModel.IsValidAnnotation); + } + + [Test] + public void AnnotationNotNull_WithArgumentsWithoutError_Valid() + { + var mockArgumentFactory = MockArgumentFactory(new List { false }); + + var viewModel = TestViewModel(DeclarationType.Procedure, mockArgumentFactory.Object); + viewModel.Annotation = new TestMethodAnnotation(); + viewModel.AddAnnotationArgument.Execute(null); + + Assert.IsTrue(viewModel.IsValidAnnotation); + } + + [Test] + public void AddArgumentAddsEmptyArgumentOfAppropriateType() + { + var mockArgumentFactory = MockArgumentFactory(new List { false }); + + var viewModel = TestViewModel(DeclarationType.Procedure, mockArgumentFactory.Object); + var initialArgumentCount = viewModel.AnnotationArguments.Count; + viewModel.Annotation = new TestMethodAnnotation(); + viewModel.AddAnnotationArgument.Execute(null); + + var argumentsAdded = viewModel.AnnotationArguments.Count - initialArgumentCount; + + Assert.AreEqual(1, argumentsAdded); + mockArgumentFactory.Verify(m => m.Create(AnnotationArgumentType.Text, string.Empty), Times.Once); + } + + [Test] + public void SetAnnotation_AddsRequiredArguments() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = new DescriptionAnnotation(); + + Assert.AreEqual(1, viewModel.AnnotationArguments.Count); + } + + [Test] + public void SetAnnotation_NoRequiredArguments_AddsNone() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = new TestMethodAnnotation(); + + Assert.IsFalse(viewModel.AnnotationArguments.Any()); + } + + [Test] + public void CanAddArguments_NoArguments_False() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = new DefaultMemberAnnotation(); + var canAddArgument = viewModel.AddAnnotationArgument.CanExecute(null); + + Assert.IsFalse(canAddArgument); + } + + [Test] + public void CanAddArguments_NoOptionalArguments_False() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = new DescriptionAnnotation(); + var canAddArgument = viewModel.AddAnnotationArgument.CanExecute(null); + + Assert.IsFalse(canAddArgument); + } + + [Test] + public void CanAddArguments_OptionalArgumentsNotThere_True() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = new TestMethodAnnotation(); + var canAddArgument = viewModel.AddAnnotationArgument.CanExecute(null); + + Assert.IsTrue(canAddArgument); + } + + [Test] + public void CanAddArguments_MaxArgumentsSpecified_False() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = new TestMethodAnnotation(); + var addArgumentCommand = viewModel.AddAnnotationArgument; + addArgumentCommand.Execute(null); + var canAddArgument = addArgumentCommand.CanExecute(null); + + Assert.IsFalse(canAddArgument); + } + + [Test] + public void CanRemoveArguments_OptionalArgumentsPresent_True() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = new MemberAttributeAnnotation(); + var addArgumentCommand = viewModel.AddAnnotationArgument; + addArgumentCommand.Execute(null); + var canRemoveArgument = viewModel.RemoveAnnotationArgument.CanExecute(null); + + Assert.IsTrue(canRemoveArgument); + } + + [Test] + public void CanRemoveArguments_NoOptionalArgumentsPresent_False() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = new MemberAttributeAnnotation(); + + var canRemoveArgument = viewModel.RemoveAnnotationArgument.CanExecute(null); + + Assert.IsFalse(canRemoveArgument); + } + + [Test] + public void RemoveArgument_LastArgumentRemoved() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = new IgnoreAnnotation(); + var addArgumentCommand = viewModel.AddAnnotationArgument; + addArgumentCommand.Execute(null); + addArgumentCommand.Execute(null); + + var initialArguments = viewModel.AnnotationArguments.ToList(); + viewModel.RemoveAnnotationArgument.Execute(null); + + var arguments = viewModel.AnnotationArguments.ToList(); + + Assert.AreEqual(initialArguments.Count - 1, arguments.Count); + for (var argumentIndex = 0; argumentIndex < arguments.Count; argumentIndex++) + { + Assert.AreSame(initialArguments[argumentIndex], arguments[argumentIndex]); + } + } + + [Test] + public void SetAnnotation_ResetsArguments() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = new DescriptionAnnotation(); + viewModel.Annotation = new TestMethodAnnotation(); + + Assert.IsFalse(viewModel.AnnotationArguments.Any()); + } + + [Test] + public void SetAnnotation_SetsAnnotationOnModel() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + var annotation = new DescriptionAnnotation(); + viewModel.Annotation = annotation; + + Assert.AreSame(viewModel.Model.Annotation, annotation); + } + + [Test] + public void ModelIsInputModelFromCreation() + { + var target = TestDeclaration(DeclarationType.Procedure); + var model = new AnnotateDeclarationModel(target, null); + var viewModel = new AnnotateDeclarationViewModel(model, _testAnnotations, null); + + Assert.AreSame(model, viewModel.Model); + } + + [Test] + public void DialogOK_SetsArguments() + { + var viewModel = TestViewModel(DeclarationType.Procedure); + viewModel.Annotation = new IgnoreAnnotation(); + var addArgumentCommand = viewModel.AddAnnotationArgument; + addArgumentCommand.Execute(null); + addArgumentCommand.Execute(null); + + var viewModelArguments = viewModel.AnnotationArguments.Select(argumentViewModel => argumentViewModel.Model).ToList(); + viewModel.OkButtonCommand.Execute(null); + + var modelArguments = viewModel.Model.Arguments; + + Assert.AreEqual(viewModelArguments.Count, modelArguments.Count); + } + + + private AnnotateDeclarationViewModel TestViewModel(DeclarationType targetDeclarationType, IAnnotation initialAnnotation = null) + { + var argumentFactory = MockArgumentFactory().Object; + return TestViewModel(targetDeclarationType, argumentFactory, initialAnnotation); + } + + private Mock MockArgumentFactory(IReadOnlyList hasErrorSpecifications = null) + { + var hasErrorSpecs = hasErrorSpecifications ?? new List(); + var argumentCount = 0; + + var argumentFactory = new Mock(); + argumentFactory.Setup(m => m.Create(It.IsAny(), It.IsAny())) + .Returns((AnnotationArgumentType argumentType, string argument) => + { + var hasError = argumentCount < hasErrorSpecs.Count && hasErrorSpecs[argumentCount]; + return MockArgument(argumentType, argument, hasError).Object; + }); + + return argumentFactory; + } + + private Mock MockArgument(AnnotationArgumentType argumentType, string argumentValue = null, bool hasError = false) + { + var mockArgument = new Mock(); + + mockArgument.SetupGet(m => m.HasErrors).Returns(hasError); + mockArgument.SetupGet(m => m.Model).Returns(new TypedAnnotationArgument(argumentType, argumentValue)); + + return mockArgument; + } + + private AnnotateDeclarationViewModel TestViewModel(DeclarationType targetDeclarationType, IAnnotationArgumentViewModelFactory argumentFactory, IAnnotation initialAnnotation = null) + { + var targetDeclaration = TestDeclaration(targetDeclarationType); + var model = new AnnotateDeclarationModel(targetDeclaration, initialAnnotation); + return new AnnotateDeclarationViewModel(model, _testAnnotations, argumentFactory); + } + + private Declaration TestDeclaration(DeclarationType targetDeclarationType) + { + const string code = @" +Public myVar As Variant + +'@Ignore MissingMemberAnnotationInspection +Public Sub Foo +End Sub + +'@DefaultMember +Public Function Bar +End Function +"; + var vbe = MockVbeBuilder.BuildFromSingleModule(code, ComponentType.ClassModule, out _).Object; + using (var state = MockParser.CreateAndParse(vbe)) + { + return state.DeclarationFinder.UserDeclarations(targetDeclarationType).Single(); + } + } + + private readonly IAnnotation[] _testAnnotations = + { + new IgnoreAnnotation(), + new IgnoreModuleAnnotation(), + new TestMethodAnnotation(), + new TestModuleAnnotation(), + new DefaultMemberAnnotation(), + new ExposedModuleAnnotation(), + new VariableDescriptionAnnotation(), + new DescriptionAnnotation(), + new MemberAttributeAnnotation(), + new ModuleDescriptionAnnotation(), + }; + } +} \ No newline at end of file diff --git a/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotationArgumentViewModelTests.cs b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotationArgumentViewModelTests.cs new file mode 100644 index 0000000000..5058d2fa51 --- /dev/null +++ b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotationArgumentViewModelTests.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Rubberduck.Parsing.Annotations; +using Rubberduck.Refactorings.AnnotateDeclaration; +using Rubberduck.UI.Refactorings.AnnotateDeclaration; + +namespace RubberduckTests.Refactoring.AnnotateDeclaration +{ + [TestFixture] + public class AnnotationArgumentViewModelTests + { + [Test] + [TestCase(AnnotationArgumentType.Attribute)] + [TestCase(AnnotationArgumentType.Inspection)] + [TestCase(AnnotationArgumentType.Boolean)] + [TestCase(AnnotationArgumentType.Number)] + [TestCase(AnnotationArgumentType.Text)] + public void RecognizesSingleArgumentType(AnnotationArgumentType argumentType) + { + var viewModel = TestViewModel(argumentType); + + Assert.AreEqual(1,viewModel.ApplicableArgumentTypes.Count); + Assert.AreEqual(argumentType, viewModel.ApplicableArgumentTypes.First()); + } + + [Test] + public void SplitsArgumentTypeIntoFlags() + { + var viewModel = TestViewModel(AnnotationArgumentType.Boolean | AnnotationArgumentType.Number | AnnotationArgumentType.Text); + var applicableTypes = viewModel.ApplicableArgumentTypes.ToList(); + + Assert.AreEqual(3, applicableTypes.Count); + Assert.Contains(AnnotationArgumentType.Boolean, applicableTypes); + Assert.Contains(AnnotationArgumentType.Number, applicableTypes); + Assert.Contains(AnnotationArgumentType.Text, applicableTypes); + } + + [Test] + public void InitiallySelectedArgumentTypeIsFirstApplicableOne() + { + var viewModel = TestViewModel(AnnotationArgumentType.Boolean | AnnotationArgumentType.Number | AnnotationArgumentType.Text); + + var expectedType = viewModel.ApplicableArgumentTypes.First(); + var actualType = viewModel.ArgumentType; + + Assert.AreEqual(expectedType, actualType); + } + + [Test] + public void CanEditArgumentTypeForMultipleApplicableArgumentTypes() + { + var viewModel = TestViewModel(AnnotationArgumentType.Boolean | AnnotationArgumentType.Number | AnnotationArgumentType.Text); + + Assert.IsTrue(viewModel.CanEditArgumentType); + } + + [Test] + public void CannotEditArgumentTypeForSingleApplicableArgumentType() + { + var viewModel = TestViewModel(AnnotationArgumentType.Boolean); + + Assert.IsFalse(viewModel.CanEditArgumentType); + } + + [Test] + [TestCase(AnnotationArgumentType.Attribute)] + [TestCase(AnnotationArgumentType.Inspection)] + [TestCase(AnnotationArgumentType.Boolean)] + [TestCase(AnnotationArgumentType.Number)] + [TestCase(AnnotationArgumentType.Text)] + public void EmptyArgumentsAreIllegal(AnnotationArgumentType argumentType) + { + var viewModel = TestViewModel(argumentType, initialArgument: "someText"); + viewModel.ArgumentValue = string.Empty; + + Assert.IsTrue(viewModel.HasErrors); + } + + [Test] + [TestCase(AnnotationArgumentType.Attribute)] + [TestCase(AnnotationArgumentType.Inspection)] + [TestCase(AnnotationArgumentType.Boolean)] + [TestCase(AnnotationArgumentType.Number)] + [TestCase(AnnotationArgumentType.Text)] + public void ArgumentsLongerThan511CharactersAreIllegal(AnnotationArgumentType argumentType) + { + var viewModel = TestViewModel(argumentType, initialArgument: "someText"); + viewModel.ArgumentValue = new string('s', 512); + + Assert.IsTrue(viewModel.HasErrors); + } + + [Test] + public void TextArgumentsWith511CharactersAreLegal() + { + var viewModel = TestViewModel(AnnotationArgumentType.Text, initialArgument: "someText"); + viewModel.ArgumentValue = new string('s', 511); + + Assert.IsFalse(viewModel.HasErrors); + } + + [Test] + [TestCase(AnnotationArgumentType.Attribute)] + [TestCase(AnnotationArgumentType.Inspection)] + [TestCase(AnnotationArgumentType.Boolean)] + [TestCase(AnnotationArgumentType.Number)] + [TestCase(AnnotationArgumentType.Text)] + public void NewLinesInArgumentsAreIllegal(AnnotationArgumentType argumentType) + { + var viewModel = TestViewModel(argumentType, initialArgument: "someText"); + viewModel.ArgumentValue = $"text with{Environment.NewLine}new line"; + + Assert.IsTrue(viewModel.HasErrors); + } + + [Test] + [TestCase(AnnotationArgumentType.Attribute)] + [TestCase(AnnotationArgumentType.Inspection)] + [TestCase(AnnotationArgumentType.Boolean)] + [TestCase(AnnotationArgumentType.Number)] + [TestCase(AnnotationArgumentType.Text)] + public void ControlCharactersInArgumentsAreIllegal(AnnotationArgumentType argumentType) + { + var viewModel = TestViewModel(argumentType, initialArgument: "someText"); + viewModel.ArgumentValue = "text with \u0000 control character"; + + Assert.IsTrue(viewModel.HasErrors); + } + + [Test] + [TestCase(AnnotationArgumentType.Attribute)] + [TestCase(AnnotationArgumentType.Inspection)] + [TestCase(AnnotationArgumentType.Boolean)] + [TestCase(AnnotationArgumentType.Number)] + [TestCase(AnnotationArgumentType.Text)] + public void InitialValueIsValidated(AnnotationArgumentType argumentType) + { + var viewModel = TestViewModel(argumentType, initialArgument: string.Empty); + + Assert.IsTrue(viewModel.HasErrors); + } + + [Test] + [TestCase(AnnotationArgumentType.Attribute, "VB_Exposed")] + [TestCase(AnnotationArgumentType.Inspection, "MyInspection")] + [TestCase(AnnotationArgumentType.Boolean, "True")] + [TestCase(AnnotationArgumentType.Number, "42")] + [TestCase(AnnotationArgumentType.Text, "someText")] + public void SettingValidArgumentClearsErrors(AnnotationArgumentType argumentType, string validArgument) + { + var viewModel = TestViewModel(argumentType, initialArgument: "someText", inspectionNames:new []{"MyInspection"}); + viewModel.ArgumentValue = string.Empty; + viewModel.ArgumentValue = validArgument; + + Assert.IsFalse(viewModel.HasErrors); + } + + [Test] + [TestCase(AnnotationArgumentType.Attribute, AnnotationArgumentType.Number, "VB_Exposed")] + [TestCase(AnnotationArgumentType.Inspection, AnnotationArgumentType.Attribute, "MyInspection")] + [TestCase(AnnotationArgumentType.Boolean, AnnotationArgumentType.Inspection, "True")] + [TestCase(AnnotationArgumentType.Number, AnnotationArgumentType.Boolean, "42")] + [TestCase(AnnotationArgumentType.Text, AnnotationArgumentType.Attribute, "someText")] + public void ChangingTheArgumentTypeCausesValidation_ToIllegal(AnnotationArgumentType initialArgumentType, AnnotationArgumentType newIllegalArgumentType, string initiallyLegalValue) + { + const AnnotationArgumentType allArgumentTypes = AnnotationArgumentType.Attribute + | AnnotationArgumentType.Inspection + | AnnotationArgumentType.Boolean + | AnnotationArgumentType.Number + | AnnotationArgumentType.Text; + + var viewModel = TestViewModel(allArgumentTypes, initialArgument: string.Empty, inspectionNames: new[] { "MyInspection" }); + viewModel.ArgumentType = initialArgumentType; + viewModel.ArgumentValue = initiallyLegalValue; + + viewModel.ArgumentType = newIllegalArgumentType; + + Assert.IsTrue(viewModel.HasErrors); + } + + [Test] + [TestCase(AnnotationArgumentType.Number, AnnotationArgumentType.Attribute, "VB_Exposed")] + [TestCase(AnnotationArgumentType.Number, AnnotationArgumentType.Inspection, "MyInspection")] + [TestCase(AnnotationArgumentType.Number, AnnotationArgumentType.Boolean, "True")] + [TestCase(AnnotationArgumentType.Boolean, AnnotationArgumentType.Number, "42")] + [TestCase(AnnotationArgumentType.Number, AnnotationArgumentType.Text, "someText")] + public void ChangingTheArgumentTypeCausesValidation_ToLegal(AnnotationArgumentType initialArgumentType, AnnotationArgumentType newLegalArgumentType, string initialyIllegalValueegalValue) + { + const AnnotationArgumentType allArgumentTypes = AnnotationArgumentType.Attribute + | AnnotationArgumentType.Inspection + | AnnotationArgumentType.Boolean + | AnnotationArgumentType.Number + | AnnotationArgumentType.Text; + + var viewModel = TestViewModel(allArgumentTypes, initialArgument: string.Empty, inspectionNames: new[] { "MyInspection" }); + viewModel.ArgumentType = initialArgumentType; + viewModel.ArgumentValue = initialyIllegalValueegalValue; + + viewModel.ArgumentType = newLegalArgumentType; + + Assert.IsFalse(viewModel.HasErrors); + } + + [Test] + public void ChangingTheArgumentTypeChangesItOnTheReturnedModel() + { + var viewModel = TestViewModel(AnnotationArgumentType.Text); + viewModel.ArgumentType = AnnotationArgumentType.Boolean; + + Assert.AreEqual(AnnotationArgumentType.Boolean, viewModel.Model.ArgumentType); + } + + [Test] + public void ChangingTheArgumentValueChangesItOnTheReturnedModel() + { + var viewModel = TestViewModel(AnnotationArgumentType.Text, string.Empty); + viewModel.ArgumentValue = "some Text"; + + Assert.AreEqual("some Text", viewModel.Model.Argument); + } + + private AnnotationArgumentViewModel TestViewModel( + AnnotationArgumentType argumentType, + string initialArgument = null, + IReadOnlyList inspectionNames = null) + { + var model = new TypedAnnotationArgument(argumentType, initialArgument ?? string.Empty); + return new AnnotationArgumentViewModel(model, inspectionNames ?? new List()); + } + } +} \ No newline at end of file From ed912ed7621242f43cee7efd3c76cbcfba1f5877 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 11 May 2020 20:15:57 -0700 Subject: [PATCH 16/82] Addressed comments. Added more examples --- .../Concrete/ProcedureNotUsedInspection.cs | 77 +++++++++++++++---- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs index a43a5143ce..4423e6d8f0 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using Rubberduck.CodeAnalysis.Inspections.Abstract; using Rubberduck.CodeAnalysis.Inspections.Extensions; @@ -14,8 +15,7 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete /// /// /// Unused procedures are dead code that should probably be removed. Note, a procedure may be effectively "not used" in code, but attached to some - /// Shape object in the host document: in such cases the inspection result should be ignored. An event handler procedure that isn't being - /// resolved as such, may also wrongly trigger this inspection. + /// Shape object in the host document: in such cases the inspection result should be ignored. /// /// /// Not all unused procedures can/should be removed: ignore any inspection results for @@ -24,34 +24,81 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete /// the presence or absence of user code references. /// /// - /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// /// + /// + /// + /// /// /// /// - /// + /// /// + /// + /// + /// /// @@ -90,7 +137,7 @@ public ProcedureNotUsedInspection(IDeclarationFinderProvider declarationFinderPr protected override bool IsResultDeclaration(Declaration declaration, DeclarationFinder finder) { return !declaration.References - .Any(reference => !reference.ParentScoping.Equals(declaration)) // recursive calls don't count + .Any(reference => !reference.ParentScoping.Equals(declaration)) // ignore recursive/self-referential calls && !finder.FindEventHandlers().Contains(declaration) && !IsPublicModuleMember(declaration) && !IsClassLifeCycleHandler(declaration) From c2885b28a52054dedd28b46571057138bd4b03e5 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Tue, 12 May 2020 09:14:14 -0700 Subject: [PATCH 17/82] Improved XMLDoc example for Public std module procs Identified example module as 'Macros' and added comment to reinforce why Public procedures of Standard Modules are ignored by the inspection. --- .../Inspections/Concrete/ProcedureNotUsedInspection.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs index 4423e6d8f0..1ddc2afcb5 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs @@ -45,11 +45,13 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete /// /// /// - /// + /// /// /// From 799b76ab4609ea4a7786c599f25f30676d2fcbbf Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Wed, 13 May 2020 22:40:56 +0200 Subject: [PATCH 18/82] Add code pane command for AnnotateDeclaration Also fixes a bug in the setup of CodePane.GetQualifiedSelection in the MockProjectBuilder. --- .../Extensions/DeclarationTypeExtensions.cs | 3 +- ...ePaneAnnotateDeclarationCommandMenuItem.cs | 21 ++++++ .../ParentMenus/RefactoringsParentMenu.cs | 1 + .../CodePaneAnnotateDeclarationCommand.cs | 56 +++++++++++++++ .../AnnotateDeclarationFailedNotifier.cs | 29 ++++++++ .../IntroduceParameterFailedNotifier.cs | 5 +- .../Root/RubberduckIoCInstaller.cs | 1 + .../AnnotateDeclarationRefactoring.cs | 10 +++ .../Menus/RubberduckMenus.Designer.cs | 9 +++ .../Menus/RubberduckMenus.de.resx | 3 + .../Menus/RubberduckMenus.resx | 3 + Rubberduck.Resources/RubberduckUI.Designer.cs | 9 +++ Rubberduck.Resources/RubberduckUI.de.resx | 3 + Rubberduck.Resources/RubberduckUI.resx | 4 ++ ...CodePaneAnnotateDeclarationCommandTests.cs | 68 +++++++++++++++++++ .../RefactorCodePaneCommandTestBase.cs | 13 +++- RubberduckTests/Mocks/MockProjectBuilder.cs | 2 +- .../AnnotateDeclarationRefactoringTests.cs | 15 ++++ 18 files changed, 249 insertions(+), 6 deletions(-) create mode 100644 Rubberduck.Core/UI/Command/MenuItems/CodePaneAnnotateDeclarationCommandMenuItem.cs create mode 100644 Rubberduck.Core/UI/Command/Refactorings/CodePaneAnnotateDeclarationCommand.cs create mode 100644 Rubberduck.Core/UI/Command/Refactorings/Notifiers/AnnotateDeclarationFailedNotifier.cs create mode 100644 RubberduckTests/Commands/RefactorCommands/CodePaneAnnotateDeclarationCommandTests.cs diff --git a/Rubberduck.CodeAnalysis/Inspections/Extensions/DeclarationTypeExtensions.cs b/Rubberduck.CodeAnalysis/Inspections/Extensions/DeclarationTypeExtensions.cs index 377d990fb1..c1d3914a53 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Extensions/DeclarationTypeExtensions.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Extensions/DeclarationTypeExtensions.cs @@ -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); diff --git a/Rubberduck.Core/UI/Command/MenuItems/CodePaneAnnotateDeclarationCommandMenuItem.cs b/Rubberduck.Core/UI/Command/MenuItems/CodePaneAnnotateDeclarationCommandMenuItem.cs new file mode 100644 index 0000000000..b32f5e9aaf --- /dev/null +++ b/Rubberduck.Core/UI/Command/MenuItems/CodePaneAnnotateDeclarationCommandMenuItem.cs @@ -0,0 +1,21 @@ +using Rubberduck.Parsing.VBA; +using Rubberduck.UI.Command.MenuItems.ParentMenus; +using Rubberduck.UI.Command.Refactorings; + +namespace Rubberduck.UI.Command.MenuItems +{ + public class CodePaneAnnotateDeclarationCommandMenuItem : CommandMenuItemBase + { + public CodePaneAnnotateDeclarationCommandMenuItem(CodePaneAnnotateDeclarationCommand command) + : base(command) + { } + + public override string Key => "RefactorMenu_AnnotateDeclaration"; + public override int DisplayOrder => (int)RefactoringsMenuItemDisplayOrder.AnnotateDeclaration; + + public override bool EvaluateCanExecute(RubberduckParserState state) + { + return state != null && Command.CanExecute(null); + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/RefactoringsParentMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/RefactoringsParentMenu.cs index 30b2d6951c..c6f7799e87 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/RefactoringsParentMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/RefactoringsParentMenu.cs @@ -14,6 +14,7 @@ public RefactoringsParentMenu(IEnumerable items) public enum RefactoringsMenuItemDisplayOrder { + AnnotateDeclaration, RenameIdentifier, ExtractMethod, ExtractInterface, diff --git a/Rubberduck.Core/UI/Command/Refactorings/CodePaneAnnotateDeclarationCommand.cs b/Rubberduck.Core/UI/Command/Refactorings/CodePaneAnnotateDeclarationCommand.cs new file mode 100644 index 0000000000..54f3c56d1e --- /dev/null +++ b/Rubberduck.Core/UI/Command/Refactorings/CodePaneAnnotateDeclarationCommand.cs @@ -0,0 +1,56 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.AnnotateDeclaration; +using Rubberduck.UI.Command.Refactorings.Notifiers; +using Rubberduck.VBEditor.Utility; + +namespace Rubberduck.UI.Command.Refactorings +{ + public class CodePaneAnnotateDeclarationCommand : RefactorCodePaneCommandBase + { + private readonly RubberduckParserState _state; + private readonly ISelectedDeclarationProvider _selectedDeclarationProvider; + + public CodePaneAnnotateDeclarationCommand( + AnnotateDeclarationRefactoring refactoring, + AnnotateDeclarationFailedNotifier failureNotifier, + ISelectionProvider selectionProvider, + IParserStatusProvider parserStatusProvider, + RubberduckParserState state, + ISelectedDeclarationProvider selectedDeclarationProvider) + : base(refactoring, failureNotifier, selectionProvider, parserStatusProvider) + { + _selectedDeclarationProvider = selectedDeclarationProvider; + _state = state; + + AddToCanExecuteEvaluation(SpecializedEvaluateCanExecute); + } + + private bool SpecializedEvaluateCanExecute(object parameter) + { + var target = GetTarget(); + + if (target == null) + { + return false; + } + + var targetType = target.DeclarationType; + + if (!targetType.HasFlag(DeclarationType.Member) + && !targetType.HasFlag(DeclarationType.Module) + && !targetType.HasFlag(DeclarationType.Variable) + && !targetType.HasFlag(DeclarationType.Constant)) + { + return false; + } + + return !_state.IsNewOrModified(target.QualifiedModuleName); + } + + private Declaration GetTarget() + { + return _selectedDeclarationProvider.SelectedDeclaration(); + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Command/Refactorings/Notifiers/AnnotateDeclarationFailedNotifier.cs b/Rubberduck.Core/UI/Command/Refactorings/Notifiers/AnnotateDeclarationFailedNotifier.cs new file mode 100644 index 0000000000..52c51a917a --- /dev/null +++ b/Rubberduck.Core/UI/Command/Refactorings/Notifiers/AnnotateDeclarationFailedNotifier.cs @@ -0,0 +1,29 @@ +using Rubberduck.CodeAnalysis.Inspections.Extensions; +using Rubberduck.Interaction; +using Rubberduck.Refactorings.Exceptions; +using Rubberduck.Resources; + +namespace Rubberduck.UI.Command.Refactorings.Notifiers +{ + public class AnnotateDeclarationFailedNotifier : RefactoringFailureNotifierBase + { + public AnnotateDeclarationFailedNotifier(IMessageBox messageBox) + : base(messageBox) + { } + + protected override string Caption => RubberduckUI.AnnotateDeclarationDialog_Caption; + + protected override string Message(RefactoringException exception) + { + if (exception is InvalidDeclarationTypeException invalidTypeException) + { + Logger.Warn(invalidTypeException); + return string.Format( + RubberduckUI.RefactoringFailure_AnnotateDeclaration_InvalidType, + invalidTypeException.TargetDeclaration.DeclarationType.ToLocalizedString()); + } + + return base.Message(exception); + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Command/Refactorings/Notifiers/IntroduceParameterFailedNotifier.cs b/Rubberduck.Core/UI/Command/Refactorings/Notifiers/IntroduceParameterFailedNotifier.cs index 0702269747..3abcc6cb02 100644 --- a/Rubberduck.Core/UI/Command/Refactorings/Notifiers/IntroduceParameterFailedNotifier.cs +++ b/Rubberduck.Core/UI/Command/Refactorings/Notifiers/IntroduceParameterFailedNotifier.cs @@ -2,6 +2,7 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings.Exceptions; using Rubberduck.Refactorings.Exceptions.IntroduceParameter; +using Rubberduck.CodeAnalysis.Inspections.Extensions; namespace Rubberduck.UI.Command.Refactorings.Notifiers { @@ -25,8 +26,8 @@ protected override string Message(RefactoringException exception) Logger.Warn(invalidDeclarationType); return string.Format(Resources.RubberduckUI.RefactoringFailure_InvalidDeclarationType, invalidDeclarationType.TargetDeclaration.QualifiedModuleName, - invalidDeclarationType.TargetDeclaration.DeclarationType, - DeclarationType.Variable); + invalidDeclarationType.TargetDeclaration.DeclarationType.ToLocalizedString(), + DeclarationType.Variable.ToLocalizedString()); default: return base.Message(exception); } diff --git a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs index 1c706459cb..d027a909ed 100644 --- a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs +++ b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs @@ -664,6 +664,7 @@ private Type[] RefactoringsMenuItems() { return new[] { + typeof(CodePaneAnnotateDeclarationCommandMenuItem), typeof(CodePaneRefactorRenameCommandMenuItem), typeof(RefactorExtractMethodCommandMenuItem), typeof(RefactorReorderParametersCommandMenuItem), diff --git a/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationRefactoring.cs b/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationRefactoring.cs index 054ee737e6..c248f0abab 100644 --- a/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationRefactoring.cs +++ b/Rubberduck.Refactorings/AnnotateDeclaration/AnnotateDeclarationRefactoring.cs @@ -34,6 +34,16 @@ protected override AnnotateDeclarationModel InitializeModel(Declaration target) throw new TargetDeclarationIsNullException(); } + var targetType = target.DeclarationType; + + if (!targetType.HasFlag(DeclarationType.Member) + && !targetType.HasFlag(DeclarationType.Module) + && !targetType.HasFlag(DeclarationType.Variable) + && !targetType.HasFlag(DeclarationType.Constant)) + { + throw new InvalidDeclarationTypeException(target); + } + return new AnnotateDeclarationModel(target); } diff --git a/Rubberduck.Resources/Menus/RubberduckMenus.Designer.cs b/Rubberduck.Resources/Menus/RubberduckMenus.Designer.cs index 434c2c00f0..9eaff8692b 100644 --- a/Rubberduck.Resources/Menus/RubberduckMenus.Designer.cs +++ b/Rubberduck.Resources/Menus/RubberduckMenus.Designer.cs @@ -159,6 +159,15 @@ public class RubberduckMenus { } } + /// + /// Looks up a localized string similar to Annotate Declaration. + /// + public static string RefactorMenu_AnnotateDeclaration { + get { + return ResourceManager.GetString("RefactorMenu_AnnotateDeclaration", resourceCulture); + } + } + /// /// Looks up a localized string similar to &Encapsulate Field. /// diff --git a/Rubberduck.Resources/Menus/RubberduckMenus.de.resx b/Rubberduck.Resources/Menus/RubberduckMenus.de.resx index d250dcdcc9..ae12249c6c 100644 --- a/Rubberduck.Resources/Menus/RubberduckMenus.de.resx +++ b/Rubberduck.Resources/Menus/RubberduckMenus.de.resx @@ -249,4 +249,7 @@ Enthaltenden Ordner verschieben + + Annotation hinzufügen + \ No newline at end of file diff --git a/Rubberduck.Resources/Menus/RubberduckMenus.resx b/Rubberduck.Resources/Menus/RubberduckMenus.resx index aa6ac0a59a..a9173aad6f 100644 --- a/Rubberduck.Resources/Menus/RubberduckMenus.resx +++ b/Rubberduck.Resources/Menus/RubberduckMenus.resx @@ -250,4 +250,7 @@ Move Containing Folder + + Annotate Declaration + \ No newline at end of file diff --git a/Rubberduck.Resources/RubberduckUI.Designer.cs b/Rubberduck.Resources/RubberduckUI.Designer.cs index 0b1eba6bcd..a4844df92f 100644 --- a/Rubberduck.Resources/RubberduckUI.Designer.cs +++ b/Rubberduck.Resources/RubberduckUI.Designer.cs @@ -3533,6 +3533,15 @@ public class RubberduckUI { } } + /// + /// Looks up a localized string similar to The refactoring can only annotate components, members and variables, but the target had type '{0}'.. + /// + public static string RefactoringFailure_AnnotateDeclaration_InvalidType { + get { + return ResourceManager.GetString("RefactoringFailure_AnnotateDeclaration_InvalidType", resourceCulture); + } + } + /// /// Looks up a localized string similar to Refactoring failed.. /// diff --git a/Rubberduck.Resources/RubberduckUI.de.resx b/Rubberduck.Resources/RubberduckUI.de.resx index bf4683a27f..40d604c8d8 100644 --- a/Rubberduck.Resources/RubberduckUI.de.resx +++ b/Rubberduck.Resources/RubberduckUI.de.resx @@ -1679,4 +1679,7 @@ Import abgebrochen. Annotationsargumente dürfen nicht länger als {0} Zeihen sein. + + Das Refactoring kann Annotationen nur zu Komponenten, Methoden und Variablen hinzufügen. Die Zieldeklaration hatte allerdings den Typ '{0}'. + \ No newline at end of file diff --git a/Rubberduck.Resources/RubberduckUI.resx b/Rubberduck.Resources/RubberduckUI.resx index 38a1907c40..573e6b6dff 100644 --- a/Rubberduck.Resources/RubberduckUI.resx +++ b/Rubberduck.Resources/RubberduckUI.resx @@ -1895,4 +1895,8 @@ Import aborted. Annotation arguments cannot be longer than {0} characters. {0} max allowed characters + + The refactoring can only annotate components, members and variables, but the target had type '{0}'. + {0} declaration type + \ No newline at end of file diff --git a/RubberduckTests/Commands/RefactorCommands/CodePaneAnnotateDeclarationCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/CodePaneAnnotateDeclarationCommandTests.cs new file mode 100644 index 0000000000..345f2bf787 --- /dev/null +++ b/RubberduckTests/Commands/RefactorCommands/CodePaneAnnotateDeclarationCommandTests.cs @@ -0,0 +1,68 @@ +using System; +using Moq; +using NUnit.Framework; +using Rubberduck.Interaction; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.UIContext; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.AnnotateDeclaration; +using Rubberduck.UI.Command; +using Rubberduck.UI.Command.Refactorings; +using Rubberduck.UI.Command.Refactorings.Notifiers; +using Rubberduck.VBEditor; +using Rubberduck.VBEditor.SafeComWrappers; +using Rubberduck.VBEditor.SafeComWrappers.Abstract; +using Rubberduck.VBEditor.Utility; + +namespace RubberduckTests.Commands.RefactorCommands +{ + [TestFixture] + public class CodePaneAnnotateDeclarationCommandTests : RefactorCodePaneCommandTestBase + { + [Category("Commands")] + [Test] + public void AnnotateDeclaration_CanExecute_InvalidTargetType() + { + var vbe = SetupAllowingExecution(); + vbe.ActiveCodePane.Selection = new Selection(2,2); + + Assert.IsFalse(CanExecute(vbe)); + } + + protected override CommandBase TestCommand( + IVBE vbe, RubberduckParserState state, + IRewritingManager rewritingManager, + ISelectionService selectionService) + { + var factory = new Mock().Object; + var msgBox = new Mock().Object; + + var uiDispatcherMock = new Mock(); + uiDispatcherMock + .Setup(m => m.Invoke(It.IsAny())) + .Callback((Action action) => action.Invoke()); + var userInteraction = new RefactoringUserInteraction(factory, uiDispatcherMock.Object); + + var annotationUpdater = new AnnotationUpdater(); + var annotateDeclarationAction = new AnnotateDeclarationRefactoringAction(rewritingManager, annotationUpdater); + + var selectedDeclarationProvider = new SelectedDeclarationProvider(selectionService, state); + + var refactoring = new AnnotateDeclarationRefactoring(annotateDeclarationAction, selectedDeclarationProvider, selectionService, userInteraction); + var notifier = new AnnotateDeclarationFailedNotifier(msgBox); + + return new CodePaneAnnotateDeclarationCommand(refactoring, notifier, selectionService, state, state, selectedDeclarationProvider); + } + + protected override IVBE SetupAllowingExecution() + { + const string input = + @"Public Sub Foo() +myLabel: Debug.Print ""Label""; +End Sub"; + var selection = Selection.Home; + return TestVbe(input, selection, ComponentType.ClassModule); + } + } +} \ No newline at end of file diff --git a/RubberduckTests/Commands/RefactorCommands/RefactorCodePaneCommandTestBase.cs b/RubberduckTests/Commands/RefactorCommands/RefactorCodePaneCommandTestBase.cs index 5d913c83d0..6aac8a5c38 100644 --- a/RubberduckTests/Commands/RefactorCommands/RefactorCodePaneCommandTestBase.cs +++ b/RubberduckTests/Commands/RefactorCommands/RefactorCodePaneCommandTestBase.cs @@ -7,12 +7,21 @@ public abstract class RefactorCodePaneCommandTestBase : RefactorCommandTestBase { [Category("Commands")] [Test] - public void EncapsulateField_CanExecute_NullActiveCodePane() + public void RefactoringCommand_CanExecute_ValidInput() + { + var vbe = SetupAllowingExecution(); + + Assert.IsTrue(CanExecute(vbe), GetType().FullName); + } + + [Category("Commands")] + [Test] + public void RefactoringCommand_CanExecute_NullActiveCodePane() { var vbe = SetupAllowingExecution(); vbe.ActiveCodePane = null; - Assert.IsFalse(CanExecute(vbe)); + Assert.IsFalse(CanExecute(vbe), GetType().FullName); } } } \ No newline at end of file diff --git a/RubberduckTests/Mocks/MockProjectBuilder.cs b/RubberduckTests/Mocks/MockProjectBuilder.cs index 8ef17772b4..34087d823f 100644 --- a/RubberduckTests/Mocks/MockProjectBuilder.cs +++ b/RubberduckTests/Mocks/MockProjectBuilder.cs @@ -422,7 +422,7 @@ private Mock CreateCodePaneMock(string name, Selection selection, Moc codePane.SetupReferenceEqualityIncludingHashCode(); codePane.Setup(p => p.GetQualifiedSelection()).Returns(() => { if (selection.IsEmpty()) { return null; } - return new QualifiedSelection(new QualifiedModuleName(component.Object), selection); + return new QualifiedSelection(new QualifiedModuleName(component.Object), codePane.Object.Selection); }); codePane.SetupProperty(p => p.Selection, selection); codePane.Setup(p => p.Show()); diff --git a/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringTests.cs b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringTests.cs index 906410abe8..bafd90e44c 100644 --- a/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringTests.cs +++ b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringTests.cs @@ -5,6 +5,7 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; using Rubberduck.Refactorings.AnnotateDeclaration; +using Rubberduck.Refactorings.Exceptions; using Rubberduck.VBEditor.SafeComWrappers; using Rubberduck.VBEditor.Utility; @@ -63,6 +64,20 @@ End Sub Assert.IsFalse(model.Arguments.Any()); } + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoring_InvalidTargetType_Throws() + { + const string code = @"Public Sub Foo() +myLabel: Debug.Print ""Label""; +End Sub"; + Assert.Throws(() => + InitialModel( + "myLabel", + DeclarationType.LineLabel, + ("TestModule", code, ComponentType.StandardModule))); + } + protected override IRefactoring TestRefactoring( IRewritingManager rewritingManager, RubberduckParserState state, From cae0d96471d325689d37b1ec969941d2af79211f Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Thu, 14 May 2020 01:44:33 +0200 Subject: [PATCH 19/82] Fix several bugs in the new UI for AnnotateDeclaration Also reorganizes it a bit internally because the original setup did not work as expected. In addition, default values are now set when switching the argument type. --- .../AnnotationToCodeStringConverter.cs | 31 +++++++ .../InspectionToLocalizedNameConverter.cs | 5 +- .../SpecificValueToVisibilityConverter.cs | 36 ++++++++ .../SpecificValuesToVisibilityConverter.cs | 34 +++++++ .../AnnotateDeclarationPresenter.cs | 2 +- .../AnnotateDeclarationView.xaml | 91 +++++++++++-------- .../AnnotateDeclarationViewModel.cs | 13 ++- ...ionArgumentValueCelDataTemplateSelector.cs | 28 ------ .../AnnotationArgumentViewModel.cs | 27 +++++- Rubberduck.Core/UI/ViewModelBase.cs | 10 +- .../Concrete/MemberAttributeAnnotation.cs | 2 +- .../Concrete/ModuleAttributeAnnotation.cs | 2 +- .../AnnotateDeclarationRefactoring.cs | 2 +- .../Menus/RubberduckMenus.Designer.cs | 2 +- .../Menus/RubberduckMenus.resx | 2 +- .../AnnotateDeclarationViewModelTests.cs | 2 +- .../AnnotationArgumentViewModelTests.cs | 48 ++++------ 17 files changed, 226 insertions(+), 111 deletions(-) create mode 100644 Rubberduck.Core/UI/Converters/AnnotationToCodeStringConverter.cs create mode 100644 Rubberduck.Core/UI/Converters/SpecificValueToVisibilityConverter.cs create mode 100644 Rubberduck.Core/UI/Converters/SpecificValuesToVisibilityConverter.cs delete mode 100644 Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentValueCelDataTemplateSelector.cs diff --git a/Rubberduck.Core/UI/Converters/AnnotationToCodeStringConverter.cs b/Rubberduck.Core/UI/Converters/AnnotationToCodeStringConverter.cs new file mode 100644 index 0000000000..3b7f3d6870 --- /dev/null +++ b/Rubberduck.Core/UI/Converters/AnnotationToCodeStringConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; +using Rubberduck.Parsing.Annotations; + +namespace Rubberduck.UI.Converters +{ + public class AnnotationToCodeStringConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + { + return null; + } + + if (!(value is IAnnotation annotation)) + { + throw new ArgumentException("The value must be an instance of IAnnotation.", "value"); + } + + return $"@{annotation.Name}"; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return DependencyProperty.UnsetValue; + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Converters/InspectionToLocalizedNameConverter.cs b/Rubberduck.Core/UI/Converters/InspectionToLocalizedNameConverter.cs index 209060001d..8137984bac 100644 --- a/Rubberduck.Core/UI/Converters/InspectionToLocalizedNameConverter.cs +++ b/Rubberduck.Core/UI/Converters/InspectionToLocalizedNameConverter.cs @@ -12,16 +12,15 @@ public class InspectionToLocalizedNameConverter : IValueConverter public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var inspectionName = value is IInspection inspection - ? inspection.Name + ? inspection.AnnotationName : value as string; if (inspectionName == null) { - throw new ArgumentException("The value must be an instance of IInspection or a string containing the programmatic name of an inspection.", "value"); } - return InspectionNames.ResourceManager.GetString(inspectionName, culture); + return InspectionNames.ResourceManager.GetString($"{inspectionName}Inspection", culture); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/Rubberduck.Core/UI/Converters/SpecificValueToVisibilityConverter.cs b/Rubberduck.Core/UI/Converters/SpecificValueToVisibilityConverter.cs new file mode 100644 index 0000000000..fb82a072c2 --- /dev/null +++ b/Rubberduck.Core/UI/Converters/SpecificValueToVisibilityConverter.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Windows; +using System.Windows.Data; + +namespace Rubberduck.UI.Converters +{ + public class SpecificValuesToVisibilityConverter : IValueConverter + { + public IReadOnlyCollection SpecialValues { get; set; } + public bool CollapseSpecialValues { get; set; } + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + { + return null; + } + + return SpecialValues.Contains(value) + ? SpecialValueVisibility + : OtherValueVisibility; + } + + private Visibility SpecialValueVisibility => CollapseSpecialValues ? Visibility.Collapsed : Visibility.Visible; + private Visibility OtherValueVisibility => CollapseSpecialValues ? Visibility.Visible : Visibility.Collapsed; + + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return DependencyProperty.UnsetValue; + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Converters/SpecificValuesToVisibilityConverter.cs b/Rubberduck.Core/UI/Converters/SpecificValuesToVisibilityConverter.cs new file mode 100644 index 0000000000..af072e2fa3 --- /dev/null +++ b/Rubberduck.Core/UI/Converters/SpecificValuesToVisibilityConverter.cs @@ -0,0 +1,34 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace Rubberduck.UI.Converters +{ + public class SpecificValueToVisibilityConverter : IValueConverter + { + public object SpecialValue { get; set; } + public bool CollapseSpecialValue { get; set; } + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + { + return null; + } + + return value.Equals(SpecialValue) + ? SpecialValueVisibility + : OtherValueVisibility; + } + + private Visibility SpecialValueVisibility => CollapseSpecialValue ? Visibility.Collapsed : Visibility.Visible; + private Visibility OtherValueVisibility => CollapseSpecialValue ? Visibility.Visible : Visibility.Collapsed; + + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return DependencyProperty.UnsetValue; + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationPresenter.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationPresenter.cs index 98982a105a..5f8ec2680e 100644 --- a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationPresenter.cs +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationPresenter.cs @@ -5,7 +5,7 @@ namespace Rubberduck.UI.Refactorings.AnnotateDeclaration { internal class AnnotateDeclarationPresenter : RefactoringPresenterBase, IAnnotateDeclarationPresenter { - private static readonly DialogData DialogData = DialogData.Create(RubberduckUI.AnnotateDeclarationDialog_Caption, 164, 684); + private static readonly DialogData DialogData = DialogData.Create(RubberduckUI.AnnotateDeclarationDialog_Caption, 400, 300); public AnnotateDeclarationPresenter(AnnotateDeclarationModel model, IRefactoringDialogFactory dialogFactory) : base(DialogData, model, dialogFactory) diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml index dfffb2b429..a34915f006 100644 --- a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml @@ -7,7 +7,7 @@ xmlns:local="clr-namespace:Rubberduck.UI.Refactorings.AnnotateDeclaration" xmlns:annotations="clr-namespace:Rubberduck.Parsing.Annotations;assembly=Rubberduck.Parsing" mc:Ignorable="d" - d:DesignHeight="300" d:DesignWidth="300"> + d:DesignHeight="400" d:DesignWidth="300"> @@ -17,20 +17,24 @@ + + + diff --git a/Rubberduck.Core/UI/CodeExplorer/Commands/AnnotateDeclarationCommand.cs b/Rubberduck.Core/UI/CodeExplorer/Commands/AnnotateDeclarationCommand.cs new file mode 100644 index 0000000000..a9d7852101 --- /dev/null +++ b/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 _annotateAction; + private readonly IRefactoringFailureNotifier _failureNotifier; + private readonly IRefactoringUserInteraction _userInteraction; + + public AnnotateDeclarationCommand( + AnnotateDeclarationRefactoringAction annotateAction, + AnnotateDeclarationFailedNotifier failureNotifier, + RefactoringUserInteraction userInteraction, + IVbeEvents vbeEvents, + RubberduckParserState state) + : base(vbeEvents) + { + _annotateAction = annotateAction; + _failureNotifier = failureNotifier; + _userInteraction = userInteraction; + _state = state; + + AddToCanExecuteEvaluation(SpecialEvaluateCanExecute); + } + + public override IEnumerable ApplicableNodeTypes => new[] { typeof(System.ValueTuple) }; + + private bool SpecialEvaluateCanExecute(object parameter) + { + if (parameter is System.ValueTuple 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 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); + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Converters/AnnotateDeclarationCommandCEVisibilityConverter.cs b/Rubberduck.Core/UI/Converters/AnnotateDeclarationCommandCEVisibilityConverter.cs new file mode 100644 index 0000000000..4b4c636dfd --- /dev/null +++ b/Rubberduck.Core/UI/Converters/AnnotateDeclarationCommandCEVisibilityConverter.cs @@ -0,0 +1,62 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; +using Rubberduck.Navigation.CodeExplorer; +using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.Symbols; + +namespace Rubberduck.UI.Converters +{ + public class AnnotateDeclarationCommandCEVisibilityConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (!(values[0] is IAnnotation annotation) + || !(values[1] is ICodeExplorerNode node)) + { + return Visibility.Collapsed; + } + + return ShouldBeVisible(annotation, node) + ? Visibility.Visible + : Visibility.Collapsed; + } + + private bool ShouldBeVisible(IAnnotation annotation, ICodeExplorerNode node) + { + var target = node.Declaration; + + if (target == null) + { + 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 object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Converters/AnnotateDeclarationCommandParameterToTupleConverter.cs b/Rubberduck.Core/UI/Converters/AnnotateDeclarationCommandParameterToTupleConverter.cs new file mode 100644 index 0000000000..5e73e3838e --- /dev/null +++ b/Rubberduck.Core/UI/Converters/AnnotateDeclarationCommandParameterToTupleConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using Rubberduck.Navigation.CodeExplorer; +using Rubberduck.Parsing.Annotations; + +namespace Rubberduck.UI.Converters +{ + public class AnnotateDeclarationCommandParameterToTupleConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + (IAnnotation annotation, ICodeExplorerNode model) data = ( + values[0] as IAnnotation, + values[1] as ICodeExplorerNode); + return data; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + var data = ((IAnnotation annotation, ICodeExplorerNode model))value; + return new[] { (object)data.annotation, data.model }; + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationViewModel.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationViewModel.cs index 46ad3e3540..912a27bf53 100644 --- a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationViewModel.cs +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationViewModel.cs @@ -76,7 +76,7 @@ public IAnnotation Annotation RefreshAnnotationArguments(value); OnPropertyChanged(); - OnPropertyChanged("IsValidAnnotation"); + OnPropertyChanged(nameof(IsValidAnnotation)); } } @@ -111,7 +111,7 @@ private IAnnotationArgumentViewModel InitialArgumentViewModel(AnnotationArgument private void ArgumentErrorStateChanged(object requestor, DataErrorsChangedEventArgs e) { - OnPropertyChanged("IsValidAnnotation"); + OnPropertyChanged(nameof(IsValidAnnotation)); } public CommandBase AddAnnotationArgument { get; } diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModel.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModel.cs index 4862615f88..40e344956b 100644 --- a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModel.cs +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModel.cs @@ -108,7 +108,7 @@ private void ValidateArgument() if (errors.Any()) { - SetErrors("ArgumentValue", errors); + SetErrors(nameof(ArgumentValue), errors); } else { diff --git a/Rubberduck.Resources/CodeExplorer/CodeExplorerUI.Designer.cs b/Rubberduck.Resources/CodeExplorer/CodeExplorerUI.Designer.cs index b2c6282b12..d429b14c8b 100644 --- a/Rubberduck.Resources/CodeExplorer/CodeExplorerUI.Designer.cs +++ b/Rubberduck.Resources/CodeExplorer/CodeExplorerUI.Designer.cs @@ -236,6 +236,15 @@ public class CodeExplorerUI { } } + /// + /// Looks up a localized string similar to Annotate. + /// + public static string CodeExplorer_Annotate { + get { + return ResourceManager.GetString("CodeExplorer_Annotate", resourceCulture); + } + } + /// /// Looks up a localized string similar to Rubberduck User Declarations - {0}. /// diff --git a/Rubberduck.Resources/CodeExplorer/CodeExplorerUI.de.resx b/Rubberduck.Resources/CodeExplorer/CodeExplorerUI.de.resx index 958584fe15..374a316c62 100644 --- a/Rubberduck.Resources/CodeExplorer/CodeExplorerUI.de.resx +++ b/Rubberduck.Resources/CodeExplorer/CodeExplorerUI.de.resx @@ -328,4 +328,7 @@ Fortfahren? In Ordner verschieben... + + Annotation hinzufügen + \ No newline at end of file diff --git a/Rubberduck.Resources/CodeExplorer/CodeExplorerUI.resx b/Rubberduck.Resources/CodeExplorer/CodeExplorerUI.resx index 9bb07edf7e..6c0f29452d 100644 --- a/Rubberduck.Resources/CodeExplorer/CodeExplorerUI.resx +++ b/Rubberduck.Resources/CodeExplorer/CodeExplorerUI.resx @@ -458,4 +458,7 @@ Continue? Move to Folder... + + Annotate + \ No newline at end of file diff --git a/RubberduckTests/CodeExplorer/CodeExplorerViewModelTests.cs b/RubberduckTests/CodeExplorer/CodeExplorerViewModelTests.cs index b71db5cb23..f84852412b 100644 --- a/RubberduckTests/CodeExplorer/CodeExplorerViewModelTests.cs +++ b/RubberduckTests/CodeExplorer/CodeExplorerViewModelTests.cs @@ -10,6 +10,7 @@ using Rubberduck.Navigation.CodeExplorer; using Rubberduck.VBEditor.SafeComWrappers; using Rubberduck.Interaction; +using Rubberduck.Parsing.Annotations; using Rubberduck.Parsing.UIContext; using Rubberduck.Parsing.VBA; using Rubberduck.UI.Command.ComCommands; @@ -2357,7 +2358,7 @@ public void UnparsedSetToTrue_NoProjects() dispatcher.Setup(m => m.StartTask(It.IsAny(), It.IsAny())).Returns((Action argument, TaskCreationOptions options) => Task.Factory.StartNew(argument.Invoke, options)); var viewModel = new CodeExplorerViewModel(state, null, null, null, dispatcher.Object, vbe.Object, null, - new CodeExplorerSyncProvider(vbe.Object, state, vbeEvents.Object)); + new CodeExplorerSyncProvider(vbe.Object, state, vbeEvents.Object), new List()); parser.Parse(new CancellationTokenSource()); if (parser.State.Status >= ParserState.Error) diff --git a/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs b/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs index 2c4db8455b..0eab0cae07 100644 --- a/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs +++ b/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs @@ -18,6 +18,7 @@ using Rubberduck.Parsing.UIContext; using Rubberduck.SettingsProvider; using Rubberduck.Interaction; +using Rubberduck.Parsing.Annotations; using Rubberduck.Refactorings; using Rubberduck.Refactorings.AddInterfaceImplementations; using Rubberduck.Refactorings.ExtractInterface; @@ -186,7 +187,8 @@ private void SetupViewModelAndParse() _windowSettingsProvider.Object, _uiDispatcher.Object, Vbe.Object, null, - new CodeExplorerSyncProvider(Vbe.Object, State, VbeEvents.Object)); + new CodeExplorerSyncProvider(Vbe.Object, State, VbeEvents.Object), + new List()); parser.Parse(new CancellationTokenSource()); if (parser.State.Status >= ParserState.Error) From 1b088d596ea46d089807863510a26d2922fe8c4a Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Sat, 23 May 2020 06:30:28 -0700 Subject: [PATCH 23/82] Fix XMLDoc Missing VBA Comment apostrophe --- .../Inspections/Concrete/ProcedureNotUsedInspection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs index 1ddc2afcb5..c93827cb98 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureNotUsedInspection.cs @@ -51,7 +51,7 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete /// /// Public Sub DoSomething() /// 'a public procedure in a standard module may be a macro - /// attached to a worksheet Shape or invoked by means other than user code. + /// 'attached to a worksheet Shape or invoked by means other than user code. /// End Sub /// ]]> /// From 5904fa1a530167804e894c0b93be3e2954bbd444 Mon Sep 17 00:00:00 2001 From: IvenBach Date: Sat, 23 May 2020 21:17:57 -0700 Subject: [PATCH 24/82] Clarify instancing wording notice --- .../ExtractInterface/ExtractInterfaceView.xaml | 7 ++++--- .../ExtractInterface/ExtractInterfaceViewModel.cs | 8 +------- Rubberduck.Resources/RubberduckUI.Designer.cs | 4 +++- Rubberduck.Resources/RubberduckUI.resx | 4 +++- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml index 78da578771..ea2a1ac9aa 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml @@ -58,10 +58,11 @@ + Visibility="{Binding CanChooseInterfaceInstancing, Converter={StaticResource BoolToVisibility}}"> - diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs index 8e8d303c98..484b5e2c60 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs @@ -61,13 +61,7 @@ public bool IsValidInterfaceName } } - public bool IsClassInstancingMutable - { - get - { - return Model.ImplementingClassInstancing != ClassInstancing.Public; - } - } + public bool CanChooseInterfaceInstancing => Model.ImplementingClassInstancing != ClassInstancing.Public; public IEnumerable ClassInstances => Enum.GetValues(typeof(ClassInstancing)).Cast(); diff --git a/Rubberduck.Resources/RubberduckUI.Designer.cs b/Rubberduck.Resources/RubberduckUI.Designer.cs index 3fc93d9a6c..e9449c4bb6 100644 --- a/Rubberduck.Resources/RubberduckUI.Designer.cs +++ b/Rubberduck.Resources/RubberduckUI.Designer.cs @@ -1466,7 +1466,9 @@ public class RubberduckUI { } /// - /// Looks up a localized string similar to 'Public' will be chosen. For private instancing make implementing class private also.. + /// Looks up a localized string similar to The implementing class is 'Public' mandating the interface be public as well. + ///If you require a 'Private' interface, change the classes instancing to private as well. + ///A private class can still implement a public interface.. /// public static string ExtractInterface_PublicInstancingMandatedByPublicClass { get { diff --git a/Rubberduck.Resources/RubberduckUI.resx b/Rubberduck.Resources/RubberduckUI.resx index 70d6ee4f64..ac308c8049 100644 --- a/Rubberduck.Resources/RubberduckUI.resx +++ b/Rubberduck.Resources/RubberduckUI.resx @@ -1829,6 +1829,8 @@ Import aborted. Public - 'Public' will be chosen. For private instancing make implementing class private also. + The implementing class is 'Public' mandating the interface be public as well. +If you require a 'Private' interface, change the classes instancing to private as well. +A private class can still implement a public interface. \ No newline at end of file From 7cd38ad8db16029d8c4be1a5539d13a6a3271d34 Mon Sep 17 00:00:00 2001 From: IvenBach Date: Sat, 23 May 2020 21:28:11 -0700 Subject: [PATCH 25/82] Remove unused converters Their declaration in XAML in made redundant by the ResourecDictionary. --- .../ExtractInterface/ExtractInterfaceView.xaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml index ea2a1ac9aa..fe8c94372a 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceView.xaml @@ -1,10 +1,9 @@  @@ -13,8 +12,6 @@ - - From 864167e90d8ebbb6be1e0c672347021eae1b8667 Mon Sep 17 00:00:00 2001 From: IvenBach Date: Sat, 23 May 2020 21:45:02 -0700 Subject: [PATCH 26/82] Remove unused code --- .../ExtractInterfaceViewModel.cs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs index 484b5e2c60..c8d4fff621 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs +++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/ExtractInterfaceViewModel.cs @@ -81,25 +81,6 @@ public ClassInstancing InterfaceInstancing } } - private bool isPublicInterfaceChecked = true; - public bool IsPublicInterfaceChecked - { - get => isPublicInterfaceChecked; - set - { - if (value == isPublicInterfaceChecked) - { - return; - } - - Model.InterfaceInstancing = value - ? ClassInstancing.Public - : ClassInstancing.Private; - isPublicInterfaceChecked = value; - OnPropertyChanged(); - } - } - private void ToggleSelection(bool value) { foreach (var item in Members) From 5237703a33873de81198adc4938027fa35b9ed10 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Sun, 24 May 2020 12:24:14 +0200 Subject: [PATCH 27/82] Exclude attribute annotations from AnnotateDeclaration if not applicable If there is no attributes context, the annotation does not make sanse. This is primarily a concern regarding variables, since module variables can have attributes, but local variables cannot. Accordingly, the annotation target enum is not sufficiently granular to provide all information. --- ...DeclarationCommandCEVisibilityConverter.cs | 9 ++- .../AnnotateDeclarationViewModel.cs | 7 ++- .../AnnotateDeclarationViewModelTests.cs | 56 ++++++++++++++++--- 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/Rubberduck.Core/UI/Converters/AnnotateDeclarationCommandCEVisibilityConverter.cs b/Rubberduck.Core/UI/Converters/AnnotateDeclarationCommandCEVisibilityConverter.cs index 4b4c636dfd..e16190e331 100644 --- a/Rubberduck.Core/UI/Converters/AnnotateDeclarationCommandCEVisibilityConverter.cs +++ b/Rubberduck.Core/UI/Converters/AnnotateDeclarationCommandCEVisibilityConverter.cs @@ -32,6 +32,13 @@ private bool ShouldBeVisible(IAnnotation annotation, ICodeExplorerNode node) return false; } + if (!target.DeclarationType.HasFlag(DeclarationType.Module) + && target.AttributesPassContext == null + && annotation is IAttributeAnnotation) + { + return false; + } + var targetType = target.DeclarationType; switch (annotation.Target) @@ -46,7 +53,7 @@ private bool ShouldBeVisible(IAnnotation annotation, ICodeExplorerNode node) return targetType.HasFlag(DeclarationType.Variable) || targetType.HasFlag(DeclarationType.Constant); case AnnotationTarget.General: - return true; + return !targetType.HasFlag(DeclarationType.Module); case AnnotationTarget.Identifier: return false; default: diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationViewModel.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationViewModel.cs index 912a27bf53..44da90d3d6 100644 --- a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationViewModel.cs +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationViewModel.cs @@ -35,8 +35,11 @@ IAnnotationArgumentViewModelFactory argumentFactory private static IReadOnlyList AnnotationsForDeclaration(Declaration declaration, IEnumerable annotations) { return AnnotationsForDeclarationType(declaration.DeclarationType, annotations) - .Where(annotation => annotation.AllowMultiple - || !declaration.Annotations.Any(pta => annotation.Equals(pta.Annotation))) + .Where(annotation => (annotation.AllowMultiple + || !declaration.Annotations.Any(pta => annotation.Equals(pta.Annotation))) + && (declaration.DeclarationType.HasFlag(DeclarationType.Module) + || declaration.AttributesPassContext != null + || !(annotation is IAttributeAnnotation))) .ToList(); } diff --git a/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationViewModelTests.cs b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationViewModelTests.cs index 112ebfa570..4ef5d06e0f 100644 --- a/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationViewModelTests.cs +++ b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationViewModelTests.cs @@ -53,7 +53,40 @@ public void AnnotationAlreadyPresent_AllowsMultiple_InApplicableAnnotations() Assert.True(applicableAnnotationNames.Contains("Ignore")); } - + + [Test] + public void AttributeAnnotation_NoAttributesContext_NoModule_NotInApplicableAnnotations() + { + var viewModel = TestViewModel(DeclarationType.Variable, localScope: true); + var applicableAnnotationNames = viewModel.ApplicableAnnotations + .Select(annotation => annotation.Name) + .ToList(); + + Assert.False(applicableAnnotationNames.Contains("VariableDescription")); + } + + [Test] + public void AttributeAnnotation_NoAttributesContext_IsModule_InApplicableAnnotations() + { + var viewModel = TestViewModel(DeclarationType.Module); + var applicableAnnotationNames = viewModel.ApplicableAnnotations + .Select(annotation => annotation.Name) + .ToList(); + + Assert.True(applicableAnnotationNames.Contains("Exposed")); + } + + [Test] + public void AttributeAnnotation_HasAttributesContext_NotInApplicableAnnotations() + { + var viewModel = TestViewModel(DeclarationType.Variable, localScope: false); + var applicableAnnotationNames = viewModel.ApplicableAnnotations + .Select(annotation => annotation.Name) + .ToList(); + + Assert.True(applicableAnnotationNames.Contains("VariableDescription")); + } + [Test] public void AnnotationNull_Invalid() { @@ -264,10 +297,10 @@ public void DialogOK_SetsArguments() } - private AnnotateDeclarationViewModel TestViewModel(DeclarationType targetDeclarationType, IAnnotation initialAnnotation = null) + private AnnotateDeclarationViewModel TestViewModel(DeclarationType targetDeclarationType, IAnnotation initialAnnotation = null, bool localScope = false) { var argumentFactory = MockArgumentFactory().Object; - return TestViewModel(targetDeclarationType, argumentFactory, initialAnnotation); + return TestViewModel(targetDeclarationType, argumentFactory, initialAnnotation, localScope); } private Mock MockArgumentFactory(IReadOnlyList hasErrorSpecifications = null) @@ -296,20 +329,21 @@ private Mock MockArgument(AnnotationArgumentType a return mockArgument; } - private AnnotateDeclarationViewModel TestViewModel(DeclarationType targetDeclarationType, IAnnotationArgumentViewModelFactory argumentFactory, IAnnotation initialAnnotation = null) + private AnnotateDeclarationViewModel TestViewModel(DeclarationType targetDeclarationType, IAnnotationArgumentViewModelFactory argumentFactory, IAnnotation initialAnnotation = null, bool localScope = false) { - var targetDeclaration = TestDeclaration(targetDeclarationType); + var targetDeclaration = TestDeclaration(targetDeclarationType, localScope); var model = new AnnotateDeclarationModel(targetDeclaration, initialAnnotation); return new AnnotateDeclarationViewModel(model, _testAnnotations, argumentFactory); } - private Declaration TestDeclaration(DeclarationType targetDeclarationType) + private Declaration TestDeclaration(DeclarationType targetDeclarationType, bool localScope = false) { const string code = @" Public myVar As Variant '@Ignore MissingMemberAnnotationInspection Public Sub Foo +Dim bar As Variant End Sub '@DefaultMember @@ -319,7 +353,15 @@ End Function var vbe = MockVbeBuilder.BuildFromSingleModule(code, ComponentType.ClassModule, out _).Object; using (var state = MockParser.CreateAndParse(vbe)) { - return state.DeclarationFinder.UserDeclarations(targetDeclarationType).Single(); + if (localScope) + { + return state.DeclarationFinder.UserDeclarations(targetDeclarationType) + .Single(declaration => declaration.ParentScopeDeclaration.DeclarationType.HasFlag(DeclarationType.Member)); + } + + return state.DeclarationFinder.UserDeclarations(targetDeclarationType) + .Single(declaration => declaration.DeclarationType.HasFlag(DeclarationType.Module) + || declaration.ParentScopeDeclaration.DeclarationType.HasFlag(DeclarationType.Module)); } } From dcbb979a380dd79bd50e81172ab4084378ecd4c1 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Sun, 24 May 2020 13:10:07 +0200 Subject: [PATCH 28/82] Sort inspections by localized name in AnnotateDeclaration UI The way it is set up, the ordering will not react to a change in UI culture, but that is OK since the dialog is modal; no possibility to get to the settings dialog during its lifetime. --- .../AnnotateDeclarationView.xaml | 7 +------ .../AnnotationArgumentViewModel.cs | 16 +++++++++++++--- .../AnnotationArgumentViewModelFactory.cs | 12 ++++++++---- .../AnnotationArgumentViewModelTests.cs | 4 +++- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml index 93e8575987..b7037bcf20 100644 --- a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml @@ -75,12 +75,7 @@ - - - - - - + _inspectionNames; + private readonly IValueConverter _inspectionNameConverter; - public AnnotationArgumentViewModel(TypedAnnotationArgument model, IReadOnlyList inspectionNames) + public AnnotationArgumentViewModel(TypedAnnotationArgument model, IReadOnlyList inspectionNames, InspectionToLocalizedNameConverter inspectionNameConverter) { _model = model; + _inspectionNameConverter = inspectionNameConverter; + _inspectionNames = inspectionNames; ApplicableArgumentTypes = ApplicableTypes(_model.ArgumentType); - InspectionNames = inspectionNames; BooleanValues = new List { "True", "False" }; _model.ArgumentType = ApplicableArgumentTypes.FirstOrDefault(); @@ -53,9 +60,12 @@ private IReadOnlyList ApplicableTypes(AnnotationArgument public IReadOnlyList ApplicableArgumentTypes { get; } public bool CanEditArgumentType => ApplicableArgumentTypes.Count > 1; - public IReadOnlyList InspectionNames { get; } public IReadOnlyList BooleanValues { get; } + public IReadOnlyList InspectionNames => _inspectionNames + .OrderBy(inspectionName => _inspectionNameConverter.Convert(inspectionName, typeof(TextBlock), null, CultureInfo.CurrentUICulture)) + .ToList(); + public AnnotationArgumentType ArgumentType { get => _model.ArgumentType; diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModelFactory.cs b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModelFactory.cs index e3730a477d..d41ab4992e 100644 --- a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModelFactory.cs +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotationArgumentViewModelFactory.cs @@ -1,26 +1,30 @@ using System.Collections.Generic; using System.Linq; +using System.Windows.Data; using Rubberduck.CodeAnalysis.Inspections; using Rubberduck.Parsing.Annotations; using Rubberduck.Refactorings.AnnotateDeclaration; +using Rubberduck.UI.Converters; namespace Rubberduck.UI.Refactorings.AnnotateDeclaration { internal class AnnotationArgumentViewModelFactory : IAnnotationArgumentViewModelFactory { - private readonly IReadOnlyList InspectionNames; + private readonly IReadOnlyList _inspectionNames; + private readonly InspectionToLocalizedNameConverter _inspectionNameConverter; - public AnnotationArgumentViewModelFactory(IEnumerable inspections) + public AnnotationArgumentViewModelFactory(IEnumerable inspections, InspectionToLocalizedNameConverter inspectionNameConverter) { - InspectionNames = inspections + _inspectionNames = inspections .Select(inspection => inspection.AnnotationName) .ToList(); + _inspectionNameConverter = inspectionNameConverter; } public IAnnotationArgumentViewModel Create(AnnotationArgumentType argumentType, string argument = null) { var model = new TypedAnnotationArgument(argumentType, argument ?? string.Empty); - return new AnnotationArgumentViewModel(model, InspectionNames); + return new AnnotationArgumentViewModel(model, _inspectionNames, _inspectionNameConverter); } } } \ No newline at end of file diff --git a/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotationArgumentViewModelTests.cs b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotationArgumentViewModelTests.cs index cec686a88d..3d99af73b6 100644 --- a/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotationArgumentViewModelTests.cs +++ b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotationArgumentViewModelTests.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using Rubberduck.Parsing.Annotations; using Rubberduck.Refactorings.AnnotateDeclaration; +using Rubberduck.UI.Converters; using Rubberduck.UI.Refactorings.AnnotateDeclaration; namespace RubberduckTests.Refactoring.AnnotateDeclaration @@ -217,7 +218,8 @@ public void ChangingTheArgumentValueChangesItOnTheReturnedModel() IReadOnlyList inspectionNames = null) { var model = new TypedAnnotationArgument(argumentType, initialArgument ?? string.Empty); - return new AnnotationArgumentViewModel(model, inspectionNames ?? new List()); + var inspectionNameConverter = new InspectionToLocalizedNameConverter(); + return new AnnotationArgumentViewModel(model, inspectionNames ?? new List(), inspectionNameConverter); } } } \ No newline at end of file From 5189907d78cc9f340978c1bf67d9148e175a68be Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Sun, 24 May 2020 15:32:26 +0200 Subject: [PATCH 29/82] Make TextBoxErrorTemplate look like current Rename UI error --- .../AnnotateDeclarationView.xaml | 91 +++++++++---------- 1 file changed, 44 insertions(+), 47 deletions(-) diff --git a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml index b7037bcf20..7bcfa42e98 100644 --- a/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml +++ b/Rubberduck.Core/UI/Refactorings/AnnotateDeclaration/AnnotateDeclarationView.xaml @@ -25,14 +25,14 @@ - - + + - + @@ -55,10 +55,9 @@ - + @@ -77,23 +76,27 @@ - + - + - + - + @@ -116,11 +119,10 @@ diff --git a/Rubberduck.Resources/RubberduckUI.Designer.cs b/Rubberduck.Resources/RubberduckUI.Designer.cs index 2a4be83420..7915f5aff1 100644 --- a/Rubberduck.Resources/RubberduckUI.Designer.cs +++ b/Rubberduck.Resources/RubberduckUI.Designer.cs @@ -162,6 +162,15 @@ public class RubberduckUI { } } + /// + /// Looks up a localized string similar to Add/Adjust attribute. + /// + public static string AnnotateDeclarationDialog_AdjustAttributeLabel { + get { + return ResourceManager.GetString("AnnotateDeclarationDialog_AdjustAttributeLabel", resourceCulture); + } + } + /// /// Looks up a localized string similar to Annotation to Add:. /// diff --git a/Rubberduck.Resources/RubberduckUI.de.resx b/Rubberduck.Resources/RubberduckUI.de.resx index 164c8e9f03..808d9c8856 100644 --- a/Rubberduck.Resources/RubberduckUI.de.resx +++ b/Rubberduck.Resources/RubberduckUI.de.resx @@ -1722,4 +1722,7 @@ Wollen Sie fortfahren? Ordner umbenennen + + Attribut hinzufügen / anpassen + \ No newline at end of file diff --git a/Rubberduck.Resources/RubberduckUI.resx b/Rubberduck.Resources/RubberduckUI.resx index c973384aa5..37230872ca 100644 --- a/Rubberduck.Resources/RubberduckUI.resx +++ b/Rubberduck.Resources/RubberduckUI.resx @@ -1940,4 +1940,7 @@ Do you want to proceed? Rename folder + + Add/Adjust attribute + \ No newline at end of file From 1a2cef2b07119a439198d03fbb13601f151054e7 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Wed, 24 Jun 2020 23:22:18 +0200 Subject: [PATCH 55/82] Show AnnotateDeclaration UI from CE command for IAttributeAnnotations This is an exception to the rule that annotations without parameters are applied immediately. This is necessary since the option to add.adjust the corresponding attribute exists now. --- .../UI/CodeExplorer/Commands/AnnotateDeclarationCommand.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Rubberduck.Core/UI/CodeExplorer/Commands/AnnotateDeclarationCommand.cs b/Rubberduck.Core/UI/CodeExplorer/Commands/AnnotateDeclarationCommand.cs index a9d7852101..f4409a1c0d 100644 --- a/Rubberduck.Core/UI/CodeExplorer/Commands/AnnotateDeclarationCommand.cs +++ b/Rubberduck.Core/UI/CodeExplorer/Commands/AnnotateDeclarationCommand.cs @@ -133,7 +133,8 @@ protected override void OnExecute(object parameter) { var model = ModelFromParameter(annotation, target); if (!annotation.AllowedArguments.HasValue - || annotation.AllowedArguments.Value > 0) + || annotation.AllowedArguments.Value > 0 + || annotation is IAttributeAnnotation) { model = _userInteraction.UserModifiedModel(model); } From 823562b45a8e25a9848fc2a272c68065e9f89000 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Thu, 25 Jun 2020 00:18:09 +0200 Subject: [PATCH 56/82] Use correct context when adding annotations via attributes code This is hard to test since the bug fixed here only materializes when the attributes are removed in the code pane version of the code. --- Rubberduck.Parsing/VBA/AnnotationUpdater.cs | 12 ++- ...notateDeclarationRefactoringActionTests.cs | 74 +++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/Rubberduck.Parsing/VBA/AnnotationUpdater.cs b/Rubberduck.Parsing/VBA/AnnotationUpdater.cs index aa236890f4..4e4cd79d51 100644 --- a/Rubberduck.Parsing/VBA/AnnotationUpdater.cs +++ b/Rubberduck.Parsing/VBA/AnnotationUpdater.cs @@ -214,7 +214,11 @@ private void AddVariableAnnotation(IRewriteSession rewriteSession, Declaration d return; } - AddAnnotation(rewriteSession, declaration.QualifiedModuleName, declaration.Context, annotationInfo, annotationValues); + var context = rewriteSession.TargetCodeKind == CodeKind.CodePaneCode + ? declaration.Context + : declaration.AttributesPassContext; + + AddAnnotation(rewriteSession, declaration.QualifiedModuleName, context, annotationInfo, annotationValues); } private void AddMemberAnnotation(IRewriteSession rewriteSession, Declaration declaration, IAnnotation annotationInfo, IReadOnlyList annotationValues) @@ -233,7 +237,11 @@ private void AddMemberAnnotation(IRewriteSession rewriteSession, Declaration dec return; } - AddAnnotation(rewriteSession, declaration.QualifiedModuleName, declaration.Context, annotationInfo, annotationValues); + var context = rewriteSession.TargetCodeKind == CodeKind.CodePaneCode + ? declaration.Context + : declaration.AttributesPassContext; + + AddAnnotation(rewriteSession, declaration.QualifiedModuleName, context, annotationInfo, annotationValues); } public void AddAnnotation(IRewriteSession rewriteSession, IdentifierReference reference, IAnnotation annotationInfo, IReadOnlyList values = null) diff --git a/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringActionTests.cs b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringActionTests.cs index 5e80f7c753..0729dd462b 100644 --- a/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/AnnotateDeclaration/AnnotateDeclarationRefactoringActionTests.cs @@ -616,6 +616,80 @@ End Sub Assert.AreEqual(expectedCode, refactoredCode); } + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoringAction_AdjustAttributeSet_WorksWithExistingAnnotation_Module() + { + const string code = @"Attribute VB_Exposed = False +'@Folder ""MyFolder"" +'@DefaultMember +Public Sub Foo() +End Sub +"; + const string expectedCode = @"Attribute VB_Exposed = False +Attribute VB_Description = ""MyDesc"" +'@ModuleDescription ""MyDesc"" +'@Folder ""MyFolder"" +'@DefaultMember +Public Sub Foo() +End Sub +"; + Func modelBuilder = (state) => + { + var declaration = state.DeclarationFinder + .UserDeclarations(DeclarationType.Module) + .Single(); + var annotation = new ModuleDescriptionAnnotation(); + var arguments = new List + { + new TypedAnnotationArgument(AnnotationArgumentType.Text, "MyDesc") + }; + + return new AnnotateDeclarationModel(declaration, annotation, arguments, true); + }; + + var refactoredCode = RefactoredCode(code, modelBuilder); + + Assert.AreEqual(expectedCode, refactoredCode); + } + + [Test] + [Category("Refactorings")] + public void AnnotateDeclarationRefactoringAction_WorksWithExistingAnnotation_Member() + { + const string code = @"Attribute VB_Exposed = False +'@Folder ""MyFolder"" +'@DefaultMember +Public Sub Foo() +End Sub +"; + const string expectedCode = @"Attribute VB_Exposed = False +'@Folder ""MyFolder"" +'@DefaultMember +'@Description ""MyDesc"" +Public Sub Foo() +Attribute Foo.VB_Description = ""MyDesc"" +End Sub +"; + Func modelBuilder = (state) => + { + var declaration = state.DeclarationFinder + .UserDeclarations(DeclarationType.Procedure) + .Single(); + var annotation = new DescriptionAnnotation(); + var arguments = new List + { + new TypedAnnotationArgument(AnnotationArgumentType.Text, "MyDesc") + }; + + return new AnnotateDeclarationModel(declaration, annotation, arguments, true); + }; + + var refactoredCode = RefactoredCode(code, modelBuilder); + + Assert.AreEqual(expectedCode, refactoredCode); + } + protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) { var annotationUpdater = new AnnotationUpdater(state); From 14e9569891ef57e1fb9153acf0e6b118cce293b7 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Thu, 25 Jun 2020 18:15:55 +0200 Subject: [PATCH 57/82] Introduce new setting to enable drag and drop in CE I have put this into the General Settings because there are no CE settings so far and it kind of makes sense. --- Rubberduck.Core/Properties/Settings.Designer.cs | 1 + Rubberduck.Core/Settings/GeneralSettings.cs | 5 ++++- .../UI/Settings/GeneralSettings.xaml | 6 ++++-- .../UI/Settings/GeneralSettingsViewModel.cs | 17 +++++++++++++++++ Rubberduck.Resources/RubberduckUI.Designer.cs | 9 +++++++++ Rubberduck.Resources/RubberduckUI.de.resx | 3 +++ Rubberduck.Resources/RubberduckUI.resx | 3 +++ .../Settings/SettingsUI.Designer.cs | 2 +- Rubberduck.Resources/Settings/SettingsUI.resx | 2 +- 9 files changed, 43 insertions(+), 5 deletions(-) diff --git a/Rubberduck.Core/Properties/Settings.Designer.cs b/Rubberduck.Core/Properties/Settings.Designer.cs index d0e2fb1125..f5d9f8f29a 100644 --- a/Rubberduck.Core/Properties/Settings.Designer.cs +++ b/Rubberduck.Core/Properties/Settings.Designer.cs @@ -291,6 +291,7 @@ internal sealed partial class Settings : global::System.Configuration.Applicatio 10 false 0 + true false ")] diff --git a/Rubberduck.Core/Settings/GeneralSettings.cs b/Rubberduck.Core/Settings/GeneralSettings.cs index 244070161e..aac14af9fa 100644 --- a/Rubberduck.Core/Settings/GeneralSettings.cs +++ b/Rubberduck.Core/Settings/GeneralSettings.cs @@ -21,6 +21,7 @@ public interface IGeneralSettings bool UserEditedLogLevel { get; set; } int MinimumLogLevel { get; set; } bool SetDpiUnaware { get; set; } + bool EnableFolderDragAndDrop { get; set; } List EnableExperimentalFeatures { get; set; } } @@ -74,6 +75,7 @@ public int MinimumLogLevel } public bool SetDpiUnaware { get; set; } + public bool EnableFolderDragAndDrop { get; set; } public List EnableExperimentalFeatures { get; set; } = new List(); @@ -100,7 +102,8 @@ public bool Equals(GeneralSettings other) MinimumLogLevel == other.MinimumLogLevel && EnableExperimentalFeatures.Count == other.EnableExperimentalFeatures.Count && EnableExperimentalFeatures.All(other.EnableExperimentalFeatures.Contains) && - SetDpiUnaware == other.SetDpiUnaware; + SetDpiUnaware == other.SetDpiUnaware && + EnableFolderDragAndDrop == other.EnableFolderDragAndDrop; } } } \ No newline at end of file diff --git a/Rubberduck.Core/UI/Settings/GeneralSettings.xaml b/Rubberduck.Core/UI/Settings/GeneralSettings.xaml index 8d28085040..55094ccb1e 100644 --- a/Rubberduck.Core/UI/Settings/GeneralSettings.xaml +++ b/Rubberduck.Core/UI/Settings/GeneralSettings.xaml @@ -89,11 +89,13 @@ - + IsEnabled="{Binding SetDpiUnawareEnabled}" + IsChecked="{Binding SetDpiUnaware}" /> + diff --git a/Rubberduck.Core/UI/Inspections/InspectionResultsViewModel.cs b/Rubberduck.Core/UI/Inspections/InspectionResultsViewModel.cs index 21fb5c2499..475558d782 100644 --- a/Rubberduck.Core/UI/Inspections/InspectionResultsViewModel.cs +++ b/Rubberduck.Core/UI/Inspections/InspectionResultsViewModel.cs @@ -50,17 +50,37 @@ public enum InspectionResultGrouping Severity }; - public class DisplayQuickFix + public class QuickFixViewModel : ViewModelBase { public IQuickFix Fix { get; } public string Description { get; } - public ICommand Command { get; } + public ICollection Commands { get; } - public DisplayQuickFix(IQuickFix fix, IInspectionResult result, ICommand command) + public QuickFixViewModel( + IQuickFix fix, + IInspectionResult result, + IEnumerable commands) { - Command = command; Fix = fix; Description = fix.Description(result); + Commands = commands.ToList(); + } + } + + public class QuickFixCommandViewModel : ViewModelBase + { + public IQuickFix Fix { get; } + public string Key { get; } + public ICommand Command { get; } + + public QuickFixCommandViewModel( + IQuickFix fix, + string key, + ICommand command) + { + Fix = fix; + Key = key; + Command = command; } } @@ -112,15 +132,24 @@ public sealed class InspectionResultsViewModel : ViewModelBase, INavigateSelecti DisableInspectionCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteDisableInspectionCommand); QuickFixCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteQuickFixCommand, CanExecuteQuickFixCommand); - QuickFixInProcedureCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteQuickFixInProcedureCommand, _ => SelectedItem != null && _state.Status == ParserState.Ready); - QuickFixInModuleCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteQuickFixInModuleCommand, _ => SelectedItem != null && _state.Status == ParserState.Ready); - QuickFixInProjectCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteQuickFixInProjectCommand, _ => SelectedItem != null && _state.Status == ParserState.Ready); - QuickFixInAllProjectsCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteQuickFixInAllProjectsCommand, _ => SelectedItem != null && _state.Status == ParserState.Ready); + QuickFixInProcedureCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteQuickFixInProcedureCommand, CanExecuteQuickFixInProcedure); + QuickFixInModuleCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteQuickFixInModuleCommand, CanExecuteQuickFixInModule); + QuickFixInProjectCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteQuickFixInProjectCommand, CanExecuteQuickFixInProject); + QuickFixInAllProjectsCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteQuickFixInAllProjectsCommand, CanExecuteQuickFixAll); CopyResultsCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteCopyResultsCommand, CanExecuteCopyResultsCommand); OpenInspectionSettings = new DelegateCommand(LogManager.GetCurrentClassLogger(), OpenSettings); CollapseAllCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteCollapseAll); ExpandAllCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteExpandAll); + QuickFixCommands = new List<(ICommand command, string key)> + { + (QuickFixCommand,"QuickFix_Instance"), + (QuickFixInProcedureCommand,"QuickFix_ThisProcedure"), + (QuickFixInModuleCommand,"QuickFix_ThisModule"), + (QuickFixInProjectCommand,"QuickFix_ThisProject"), + (QuickFixInAllProjectsCommand,"QuickFix_All") + }; + _configService.SettingsChanged += _configService_SettingsChanged; // todo: remove I/O work in constructor @@ -155,33 +184,28 @@ private void _configService_SettingsChanged(object sender, ConfigurationChangedE public ICollectionView Results { get; } - private IQuickFix _defaultFix; - private INavigateSource _selectedItem; public INavigateSource SelectedItem { get => _selectedItem; set { + if (value == _selectedItem) + { + return; + } + _selectedItem = value; OnPropertyChanged(); OnPropertyChanged(nameof(QuickFixes)); SelectedInspection = null; CanQuickFix = false; - CanExecuteQuickFixInProcedure = false; - CanExecuteQuickFixInModule = false; - CanExecuteQuickFixInProject = false; if (_selectedItem is IInspectionResult inspectionResult) { SelectedInspection = inspectionResult.Inspection; CanQuickFix = _quickFixProvider.HasQuickFixes(inspectionResult); - _defaultFix = _quickFixProvider.QuickFixes(inspectionResult).FirstOrDefault(); - CanExecuteQuickFixInProcedure = _defaultFix != null && _defaultFix.CanFixInProcedure; - CanExecuteQuickFixInModule = _defaultFix != null && _defaultFix.CanFixInModule; - CanExecuteQuickFixInModule = _defaultFix != null && _defaultFix.CanFixInProcedure; - CanExecuteQuickFixInProject = _defaultFix != null && _defaultFix.CanFixInProject; } CanDisableInspection = SelectedInspection != null; @@ -199,20 +223,28 @@ public IInspection SelectedInspection } } - public IEnumerable QuickFixes + public IEnumerable QuickFixes { get { - if (SelectedItem == null) + if (!(SelectedItem is IInspectionResult result)) { - return Enumerable.Empty(); + return Enumerable.Empty(); } return _quickFixProvider.QuickFixes(SelectedItem as IInspectionResult) - .Select(fix => new DisplayQuickFix(fix, (IInspectionResult)_selectedItem, QuickFixCommand)); + .Select(fix => DisplayQuickFix(fix, result)); } } + private List<(ICommand command, string key)> QuickFixCommands { get; } + + private QuickFixViewModel DisplayQuickFix(IQuickFix quickFix, IInspectionResult result) + { + var commands = QuickFixCommands.Select(tpl => new QuickFixCommandViewModel(quickFix, tpl.key, tpl.command)); + return new QuickFixViewModel(quickFix, result, commands); + } + private static readonly Dictionary GroupDescriptions = new Dictionary { { InspectionResultGrouping.Type, new PropertyGroupDescription("Inspection", new InspectionTypeConverter()) }, @@ -512,78 +544,107 @@ private void InvalidateStaleInspectionResults(ICollection m private void ExecuteQuickFixCommand(object parameter) { - var quickFix = parameter as IQuickFix; - _quickFixProvider.Fix(quickFix, SelectedItem as IInspectionResult); + if (!(parameter is IQuickFix quickFix) + || !(SelectedItem is IInspectionResult inspectionResult)) + { + return; + } + + _quickFixProvider.Fix(quickFix, inspectionResult); } private bool CanExecuteQuickFixCommand(object parameter) { - return !IsBusy && parameter is IQuickFix && _state.Status == ParserState.Ready; + return !IsBusy + && parameter is IQuickFix + && _state.Status == ParserState.Ready; } - private bool _canExecuteQuickFixInProcedure; - public bool CanExecuteQuickFixInProcedure + private void ExecuteQuickFixInProcedureCommand(object parameter) { - get => _canExecuteQuickFixInProcedure; - set + if (!(parameter is IQuickFix quickFix) + || !(SelectedItem is IInspectionResult inspectionResult)) { - _canExecuteQuickFixInProcedure = value; - OnPropertyChanged(); + return; } + + _quickFixProvider.FixInProcedure( + quickFix, + inspectionResult.QualifiedMemberName, + inspectionResult.Inspection.GetType(), + Results.OfType()); } - private void ExecuteQuickFixInProcedureCommand(object parameter) + public bool CanExecuteQuickFixInProcedure(object parameter) { - if (_defaultFix == null) - { - return; - } + return CanExecuteQuickFixCommand(parameter) + && parameter is IQuickFix quickFix + && quickFix.CanFixInProcedure; + } - if (!(SelectedItem is IInspectionResult selectedResult)) + private void ExecuteQuickFixInModuleCommand(object parameter) + { + if (!(parameter is IQuickFix quickFix) + || !(SelectedItem is IInspectionResult inspectionResult)) { return; } - _quickFixProvider.FixInProcedure(_defaultFix, selectedResult.QualifiedMemberName, - selectedResult.Inspection.GetType(), Results.OfType()); + _quickFixProvider.FixInModule( + quickFix, + inspectionResult.QualifiedSelection, + inspectionResult.Inspection.GetType(), + Results.OfType()); } - private bool _canExecuteQuickFixInModule; - public bool CanExecuteQuickFixInModule + public bool CanExecuteQuickFixInModule(object parameter) { - get => _canExecuteQuickFixInModule; - set - { - _canExecuteQuickFixInModule = value; - OnPropertyChanged(); - } + return CanExecuteQuickFixCommand(parameter) + && parameter is IQuickFix quickFix + && quickFix.CanFixInModule; } - private void ExecuteQuickFixInModuleCommand(object parameter) + private void ExecuteQuickFixInProjectCommand(object parameter) { - if (_defaultFix == null) + if (!(parameter is IQuickFix quickFix) + || !(SelectedItem is IInspectionResult inspectionResult)) { return; } - if (!(SelectedItem is IInspectionResult selectedResult)) + _quickFixProvider.FixInProject( + quickFix, + inspectionResult.QualifiedSelection, + inspectionResult.Inspection.GetType(), + Results.OfType()); + } + + public bool CanExecuteQuickFixInProject(object parameter) + { + return CanExecuteQuickFixCommand(parameter) + && parameter is IQuickFix quickFix + && quickFix.CanFixInProject; + } + + private void ExecuteQuickFixInAllProjectsCommand(object parameter) + { + if (!(parameter is IQuickFix quickFix) + || !(SelectedItem is IInspectionResult inspectionResult)) { return; } - - _quickFixProvider.FixInModule(_defaultFix, selectedResult.QualifiedSelection, - selectedResult.Inspection.GetType(), Results.OfType()); + + _quickFixProvider.FixAll( + quickFix, + inspectionResult.Inspection.GetType(), + Results.OfType()); } - private bool _canExecuteQuickFixInProject; - public bool CanExecuteQuickFixInProject + public bool CanExecuteQuickFixAll(object parameter) { - get => _canExecuteQuickFixInProject; - set - { - _canExecuteQuickFixInProject = value; - OnPropertyChanged(); - } + return CanExecuteQuickFixCommand(parameter) + && parameter is IQuickFix quickFix + && quickFix.CanFixAll; } private void ExecuteDisableInspectionCommand(object parameter) @@ -616,37 +677,6 @@ public bool CanDisableInspection } } - private void ExecuteQuickFixInProjectCommand(object parameter) - { - if (_defaultFix == null) - { - return; - } - - if (!(SelectedItem is IInspectionResult selectedResult)) - { - return; - } - - _quickFixProvider.FixInProject(_defaultFix, selectedResult.QualifiedSelection, - selectedResult.Inspection.GetType(), Results.OfType()); - } - - private void ExecuteQuickFixInAllProjectsCommand(object parameter) - { - if (_defaultFix == null) - { - return; - } - - if (!(SelectedItem is IInspectionResult selectedResult)) - { - return; - } - - _quickFixProvider.FixAll(_defaultFix, selectedResult.Inspection.GetType(), Results.OfType()); - } - private static readonly List<(string Name, hAlignment alignment)> ResultColumns = new List<(string Name, hAlignment alignment)> { (Resources.Inspections.InspectionsUI.ExportColumnHeader_Type, hAlignment.Left), diff --git a/Rubberduck.Core/UI/Inspections/InspectionsUIResourceKeyConverter.cs b/Rubberduck.Core/UI/Inspections/InspectionsUIResourceKeyConverter.cs new file mode 100644 index 0000000000..69b57684e8 --- /dev/null +++ b/Rubberduck.Core/UI/Inspections/InspectionsUIResourceKeyConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; +using Rubberduck.Resources.Inspections; + +namespace Rubberduck.UI.Inspections +{ + public class InspectionsUIResourceKeyConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var key = value as string; + + if (key == null) + { + throw new ArgumentException("The value must be a string containing a key in the InspectionsUI resource.", "value"); + } + + return InspectionsUI.ResourceManager.GetString(key, culture); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return DependencyProperty.UnsetValue; + } + } +} \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/InspectionsUI.Designer.cs b/Rubberduck.Resources/Inspections/InspectionsUI.Designer.cs index 4dee07322e..75f83c65ba 100644 --- a/Rubberduck.Resources/Inspections/InspectionsUI.Designer.cs +++ b/Rubberduck.Resources/Inspections/InspectionsUI.Designer.cs @@ -355,6 +355,15 @@ public class InspectionsUI { } } + /// + /// Looks up a localized string similar to Fix selected occurrence. + /// + public static string QuickFix_Instance { + get { + return ResourceManager.GetString("QuickFix_Instance", resourceCulture); + } + } + /// /// Looks up a localized string similar to Fix all occurrences in module. /// diff --git a/Rubberduck.Resources/Inspections/InspectionsUI.de.resx b/Rubberduck.Resources/Inspections/InspectionsUI.de.resx index d0dfa20800..8e09d1f80a 100644 --- a/Rubberduck.Resources/Inspections/InspectionsUI.de.resx +++ b/Rubberduck.Resources/Inspections/InspectionsUI.de.resx @@ -201,4 +201,7 @@ Schweregrad + + Ausgewähltes Vorkommnis beheben + \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/InspectionsUI.resx b/Rubberduck.Resources/Inspections/InspectionsUI.resx index 03c6bf42e5..7c84292580 100644 --- a/Rubberduck.Resources/Inspections/InspectionsUI.resx +++ b/Rubberduck.Resources/Inspections/InspectionsUI.resx @@ -229,4 +229,7 @@ Severity + + Fix selected occurrence + \ No newline at end of file From 7f35af8d37279c6dba079d405c2d26ab47abcd49 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Fri, 26 Jun 2020 19:23:56 +0200 Subject: [PATCH 60/82] Introduce IgnoreInModuleQuickFix This adds an IgnoreModule annotation for the inspection corresponding to the result. --- .../Concrete/IgnoreInModuleQuickFix.cs | 103 +++++++++++++ .../QuickFixes/Concrete/IgnoreOnceQuickFix.cs | 4 +- .../QuickFixImageSourceConverter.cs | 4 +- .../Inspections/QuickFixes.Designer.cs | 9 ++ .../Inspections/QuickFixes.de.resx | 3 + .../Inspections/QuickFixes.resx | 3 + .../QuickFixes/IgnoreInModuleQuickFixTests.cs | 135 ++++++++++++++++++ 7 files changed, 258 insertions(+), 3 deletions(-) create mode 100644 Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreInModuleQuickFix.cs create mode 100644 RubberduckTests/QuickFixes/IgnoreInModuleQuickFixTests.cs diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreInModuleQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreInModuleQuickFix.cs new file mode 100644 index 0000000000..e19dc1691b --- /dev/null +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreInModuleQuickFix.cs @@ -0,0 +1,103 @@ +using System.Collections.Generic; +using System.Linq; +using Rubberduck.CodeAnalysis.Inspections; +using Rubberduck.CodeAnalysis.Inspections.Attributes; +using Rubberduck.CodeAnalysis.QuickFixes.Abstract; +using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; + +namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete +{ + /// + /// Adds an '@IgnoreModule annotation to ignore a inspection results for a specific inspection inside a whole module. Applicable to all inspections whose results can be annotated in a module. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal sealed class IgnoreInModuleQuickFix : QuickFixBase + { + private readonly RubberduckParserState _state; + private readonly IAnnotationUpdater _annotationUpdater; + + public IgnoreInModuleQuickFix(IAnnotationUpdater annotationUpdater, RubberduckParserState state, IEnumerable inspections) + : base(inspections.Select(s => s.GetType()).Where(i => i.CustomAttributes.All(a => a.AttributeType != typeof(CannotAnnotateAttribute))).ToArray()) + { + _state = state; + _annotationUpdater = annotationUpdater; + } + + public override bool CanFixInProcedure => false; + public override bool CanFixInModule => true; + public override bool CanFixInProject => true; + public override bool CanFixAll => true; + + public override void Fix(IInspectionResult result, IRewriteSession rewriteSession) + { + var module = result.Target.QualifiedModuleName; + var moduleDeclaration = _state.DeclarationFinder.Members(module, DeclarationType.Module) + .FirstOrDefault(); + + if (moduleDeclaration == null) + { + return; + } + + var existingIgnoreModuleAnnotation = moduleDeclaration.Annotations + .FirstOrDefault(pta => pta.Annotation is IgnoreModuleAnnotation); + + var annotationType = new IgnoreModuleAnnotation(); + if (existingIgnoreModuleAnnotation != null) + { + var annotationValues = existingIgnoreModuleAnnotation.AnnotationArguments.ToList(); + + if (annotationValues.Contains(result.Inspection.AnnotationName)) + { + return; + } + + annotationValues.Insert(0, result.Inspection.AnnotationName); + _annotationUpdater.UpdateAnnotation(rewriteSession, existingIgnoreModuleAnnotation, annotationType, annotationValues); + } + else + { + var newModuleText = rewriteSession.CheckOutModuleRewriter(module).GetText(); + var ignoreModuleText = $"'{ParseTreeAnnotation.ANNOTATION_MARKER}{annotationType.Name}"; + if (newModuleText.Contains(ignoreModuleText)) + { + //Most probably, we have added this already in another invocation on the same rewrite session. + return; + } + + var annotationValues = new List { result.Inspection.AnnotationName }; + _annotationUpdater.AddAnnotation(rewriteSession, moduleDeclaration, annotationType, annotationValues); + } + } + + public override string Description(IInspectionResult result) => Resources.Inspections.QuickFixes.IgnoreInModuleQuickFix; + } +} \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreOnceQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreOnceQuickFix.cs index b44ebf7cbc..496cf22f9e 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreOnceQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreOnceQuickFix.cs @@ -51,6 +51,7 @@ public IgnoreOnceQuickFix(IAnnotationUpdater annotationUpdater, RubberduckParser public override bool CanFixInProcedure => false; public override bool CanFixInModule => false; public override bool CanFixInProject => false; + public override bool CanFixAll => false; public override void Fix(IInspectionResult result, IRewriteSession rewriteSession) { @@ -90,8 +91,7 @@ private void FixModule(IInspectionResult result, IRewriteSession rewriteSession) { var moduleDeclaration = result.Target; var existingIgnoreModuleAnnotation = moduleDeclaration.Annotations - .Where(pta => pta.Annotation is IgnoreModuleAnnotation) - .FirstOrDefault(); + .FirstOrDefault(pta => pta.Annotation is IgnoreModuleAnnotation); var annotationType = new IgnoreModuleAnnotation(); if (existingIgnoreModuleAnnotation != null) diff --git a/Rubberduck.Core/UI/Inspections/QuickFixImageSourceConverter.cs b/Rubberduck.Core/UI/Inspections/QuickFixImageSourceConverter.cs index ceb5380e37..78f7744aba 100644 --- a/Rubberduck.Core/UI/Inspections/QuickFixImageSourceConverter.cs +++ b/Rubberduck.Core/UI/Inspections/QuickFixImageSourceConverter.cs @@ -12,7 +12,9 @@ public class QuickFixImageSourceConverter : ImageSourceConverter public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (value != null && value.GetType().Name.Equals("IgnoreOnceQuickFix")) + if (value != null + && (value.GetType().Name.Equals("IgnoreOnceQuickFix") + || value.GetType().Name.Equals("IgnoreInModuleQuickFix"))) { return IgnoreOnceIcon; } diff --git a/Rubberduck.Resources/Inspections/QuickFixes.Designer.cs b/Rubberduck.Resources/Inspections/QuickFixes.Designer.cs index fa21345f11..bc7ddf786e 100644 --- a/Rubberduck.Resources/Inspections/QuickFixes.Designer.cs +++ b/Rubberduck.Resources/Inspections/QuickFixes.Designer.cs @@ -213,6 +213,15 @@ public class QuickFixes { } } + /// + /// Looks up a localized string similar to Ignore in module. + /// + public static string IgnoreInModuleQuickFix { + get { + return ResourceManager.GetString("IgnoreInModuleQuickFix", resourceCulture); + } + } + /// /// Looks up a localized string similar to Ignore once. /// diff --git a/Rubberduck.Resources/Inspections/QuickFixes.de.resx b/Rubberduck.Resources/Inspections/QuickFixes.de.resx index 21b0f5f5f7..062010983b 100644 --- a/Rubberduck.Resources/Inspections/QuickFixes.de.resx +++ b/Rubberduck.Resources/Inspections/QuickFixes.de.resx @@ -297,4 +297,7 @@ Füge einen expliziten Zugriff auf den Standardmember ein + + In Modul ignorieren + \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/QuickFixes.resx b/Rubberduck.Resources/Inspections/QuickFixes.resx index 998f152944..4157a079e6 100644 --- a/Rubberduck.Resources/Inspections/QuickFixes.resx +++ b/Rubberduck.Resources/Inspections/QuickFixes.resx @@ -297,4 +297,7 @@ Make default member access explicit + + Ignore in module + \ No newline at end of file diff --git a/RubberduckTests/QuickFixes/IgnoreInModuleQuickFixTests.cs b/RubberduckTests/QuickFixes/IgnoreInModuleQuickFixTests.cs new file mode 100644 index 0000000000..48ab6a048e --- /dev/null +++ b/RubberduckTests/QuickFixes/IgnoreInModuleQuickFixTests.cs @@ -0,0 +1,135 @@ +using System.Collections.Generic; +using NUnit.Framework; +using Rubberduck.CodeAnalysis.Inspections; +using Rubberduck.CodeAnalysis.Inspections.Concrete; +using Rubberduck.CodeAnalysis.QuickFixes; +using Rubberduck.CodeAnalysis.QuickFixes.Concrete; +using Rubberduck.Parsing.VBA; + +namespace RubberduckTests.QuickFixes +{ + [TestFixture] + public class IgnoreInModuleQuickFixTests : QuickFixTestBase + { + [Test] + [Category("QuickFixes")] + public void NoIgnoreModule_AddsNewOne() + { + var inputCode = + @" +Public Sub DoSomething() + Dim value As Long + Dim bar As Long + value = 42 + bar = 23 + Debug.Print 42 +End Sub"; + + var expectedCode = + @"'@IgnoreModule VariableNotUsed + +Public Sub DoSomething() + Dim value As Long + Dim bar As Long + value = 42 + bar = 23 + Debug.Print 42 +End Sub"; + + var actualCode = ApplyQuickFixToFirstInspectionResult(inputCode, state => new VariableNotUsedInspection(state)); + Assert.AreEqual(expectedCode, actualCode); + } + + [Test] + [Category("QuickFixes")] + public void IgnoreModuleAlreadyThere_InspectionNotIgnoredYet_AddsNewArgument() + { + var inputCode = + @"'@IgnoreModule AssignmentNotUsed +Public Sub DoSomething() + Dim value As Long + Dim bar As Long + value = 42 + bar = 23 + Debug.Print 42 +End Sub"; + + var expectedCode = + @"'@IgnoreModule VariableNotUsed, AssignmentNotUsed +Public Sub DoSomething() + Dim value As Long + Dim bar As Long + value = 42 + bar = 23 + Debug.Print 42 +End Sub"; + + var actualCode = ApplyQuickFixToFirstInspectionResult(inputCode, state => new VariableNotUsedInspection(state)); + Assert.AreEqual(expectedCode, actualCode); + } + + [Test] + [Category("QuickFixes")] + public void IgnoreModuleMultiple_AddsOneAnnotation_NoIgnoreModuleYet() + { + var inputCode = + @" +Public Sub DoSomething() + Dim value As Long + Dim bar As Long + value = 42 + bar = 23 + Debug.Print 42 +End Sub"; + + var expectedCode = + @"'@IgnoreModule VariableNotUsed + +Public Sub DoSomething() + Dim value As Long + Dim bar As Long + value = 42 + bar = 23 + Debug.Print 42 +End Sub"; + + var actualCode = ApplyQuickFixToAllInspectionResults(inputCode, state => new VariableNotUsedInspection(state)); + Assert.AreEqual(expectedCode, actualCode); + } + + [Test] + [Category("QuickFixes")] + public void IgnoreModuleMultiple_AddsOneAnnotation_IgnoreModuleAlreadyThere() + { + var inputCode = + @"'@IgnoreModule AssignmentNotUsed +Public Sub DoSomething() + Dim value As Long + Dim bar As Long + value = 42 + bar = 23 + Debug.Print 42 +End Sub"; + + var expectedCode = + @"'@IgnoreModule VariableNotUsed, AssignmentNotUsed +Public Sub DoSomething() + Dim value As Long + Dim bar As Long + value = 42 + bar = 23 + Debug.Print 42 +End Sub"; + + var actualCode = ApplyQuickFixToAllInspectionResults(inputCode, state => new VariableNotUsedInspection(state)); + Assert.AreEqual(expectedCode, actualCode); + } + + protected override IQuickFix QuickFix(RubberduckParserState state) + { + var annotationUpdater = new AnnotationUpdater(); + var inspections = new List {new VariableNotUsedInspection(state)}; + return new IgnoreInModuleQuickFix(annotationUpdater, state, inspections); + } + } +} \ No newline at end of file From 77d8a23190c1c2b886565c68cc3500f7b7d46a2a Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Fri, 26 Jun 2020 19:24:53 +0200 Subject: [PATCH 61/82] Allow to apply IgnoreOnce in bulk --- .../QuickFixes/Concrete/IgnoreInModuleQuickFix.cs | 2 +- .../QuickFixes/Concrete/IgnoreOnceQuickFix.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreInModuleQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreInModuleQuickFix.cs index e19dc1691b..653e050fa3 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreInModuleQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreInModuleQuickFix.cs @@ -13,7 +13,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// Adds an '@IgnoreModule annotation to ignore a inspection results for a specific inspection inside a whole module. Applicable to all inspections whose results can be annotated in a module. /// - /// + /// /// /// /// /// Adds an '@Ignore annotation to ignore a specific inspection result. Applicable to all inspections whose results can be annotated in a module. /// - /// + /// /// /// /// false; - public override bool CanFixInModule => false; - public override bool CanFixInProject => false; - public override bool CanFixAll => false; + public override bool CanFixInProcedure => true; + public override bool CanFixInModule => true; + public override bool CanFixInProject => true; + public override bool CanFixAll => true; public override void Fix(IInspectionResult result, IRewriteSession rewriteSession) { From 34ff6c4bf6c3ec12792912c306451985d880bf95 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Fri, 26 Jun 2020 21:05:03 +0200 Subject: [PATCH 62/82] Add CanFixMultiple flag to quick fixes This is in preparation of fix selected items. --- .../QuickFixes/Abstract/QuickFixBase.cs | 4 ++-- .../QuickFixes/Abstract/RefactoringQuickFixBase.cs | 2 ++ .../Concrete/AccessSheetUsingCodeNameQuickFix.cs | 4 +++- .../Concrete/AddAttributeAnnotationQuickFix.cs | 4 +++- .../Concrete/AddIdentifierToWhiteListQuickFix.cs | 10 ++++++---- .../QuickFixes/Concrete/AddMissingAttributeQuickFix.cs | 4 +++- .../QuickFixes/Concrete/AddStepOneQuickFix.cs | 6 +++--- .../Concrete/AdjustAttributeAnnotationQuickFix.cs | 4 +++- .../Concrete/AdjustAttributeValuesQuickFix.cs | 4 +++- .../Concrete/ApplicationWorksheetFunctionQuickFix.cs | 4 +++- .../AssignedByValParameterMakeLocalCopyQuickFix.cs | 4 +++- .../QuickFixes/Concrete/ChangeDimToPrivateQuickFix.cs | 4 +++- .../QuickFixes/Concrete/ChangeIntegerToLongQuickFix.cs | 4 +++- .../Concrete/ChangeProcedureToFunctionQuickFix.cs | 4 +++- .../QuickFixes/Concrete/ConvertToProcedureQuickFix.cs | 4 +++- .../Concrete/DeclareAsExplicitVariantQuickFix.cs | 4 +++- .../QuickFixes/Concrete/ExpandBangNotationQuickFix.cs | 4 +++- .../QuickFixes/Concrete/ExpandDefaultMemberQuickFix.cs | 4 +++- .../QuickFixes/Concrete/IgnoreInModuleQuickFix.cs | 3 ++- .../QuickFixes/Concrete/IgnoreOnceQuickFix.cs | 3 ++- .../Concrete/IntroduceLocalVariableQuickFix.cs | 4 +++- .../IsMissingOnInappropriateArgumentQuickFix.cs | 4 +++- .../Concrete/MakeSingleLineParameterQuickFix.cs | 4 +++- .../QuickFixes/Concrete/OptionExplicitQuickFix.cs | 5 +++-- .../Concrete/PassParameterByReferenceQuickFix.cs | 4 +++- .../Concrete/PassParameterByValueQuickFix.cs | 4 +++- .../Concrete/Refactoring/EncapsulateFieldQuickFix.cs | 2 +- .../Refactoring/MoveFieldCloserToUsageQuickFix.cs | 2 +- .../Refactoring/RemoveUnusedParameterQuickFix.cs | 2 +- .../Concrete/Refactoring/RenameDeclarationQuickFix.cs | 2 +- .../QuickFixes/Concrete/RemoveAnnotationQuickFix.cs | 4 +++- .../QuickFixes/Concrete/RemoveAttributeQuickFix.cs | 4 +++- .../QuickFixes/Concrete/RemoveCommentQuickFix.cs | 4 +++- .../Concrete/RemoveDuplicatedAnnotationQuickFix.cs | 4 +++- .../Concrete/RemoveEmptyElseBlockQuickFix.cs | 4 +++- .../QuickFixes/Concrete/RemoveEmptyIfBlockQuickFix.cs | 4 +++- .../Concrete/RemoveExplicitByRefModifierQuickFix.cs | 4 +++- .../Concrete/RemoveExplicitCallStatementQuickFix.cs | 4 +++- .../Concrete/RemoveExplicitLetStatementQuickFix.cs | 4 +++- .../QuickFixes/Concrete/RemoveLocalErrorQuickFix.cs | 4 +++- .../Concrete/RemoveOptionBaseStatementQuickFix.cs | 4 +++- .../QuickFixes/Concrete/RemoveStepOneQuickFix.cs | 6 +++--- .../QuickFixes/Concrete/RemoveStopKeywordQuickFix.cs | 4 +++- .../QuickFixes/Concrete/RemoveTypeHintsQuickFix.cs | 4 +++- .../Concrete/RemoveUnassignedIdentifierQuickFix.cs | 4 +++- .../Concrete/RemoveUnassignedVariableUsageQuickFix.cs | 4 +++- .../Concrete/RemoveUnusedDeclarationQuickFix.cs | 4 +++- .../ReplaceEmptyStringLiteralStatementQuickFix.cs | 4 +++- .../Concrete/ReplaceGlobalModifierQuickFix.cs | 4 +++- .../ReplaceIfElseWithConditionalStatementQuickFix.cs | 4 +++- .../Concrete/ReplaceObsoleteCommentMarkerQuickFix.cs | 4 +++- .../Concrete/ReplaceObsoleteErrorStatementQuickFix.cs | 4 +++- .../ReplaceWhileWendWithDoWhileLoopQuickFix.cs | 4 +++- .../Concrete/RestoreErrorHandlingQuickFix.cs | 4 +++- .../Concrete/SetExplicitVariantReturnTypeQuickFix.cs | 4 +++- .../Concrete/SpecifyExplicitByRefModifierQuickFix.cs | 4 +++- .../Concrete/SpecifyExplicitPublicModifierQuickFix.cs | 4 +++- .../Concrete/SplitMultipleDeclarationsQuickFix.cs | 4 +++- .../Concrete/UntypedFunctionUsageQuickFix.cs | 4 +++- .../UseSetKeywordForObjectAssignmentQuickFix.cs | 4 +++- .../QuickFixes/Concrete/WriteOnlyPropertyQuickFix.cs | 4 +++- Rubberduck.CodeAnalysis/QuickFixes/IQuickFix.cs | 1 + 62 files changed, 175 insertions(+), 69 deletions(-) diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Abstract/QuickFixBase.cs b/Rubberduck.CodeAnalysis/QuickFixes/Abstract/QuickFixBase.cs index deb6ff22c0..506d381b3c 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Abstract/QuickFixBase.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Abstract/QuickFixBase.cs @@ -57,10 +57,10 @@ public void RemoveInspections(params Type[] inspections) public abstract void Fix(IInspectionResult result, IRewriteSession rewriteSession); public abstract string Description(IInspectionResult result); + public abstract bool CanFixMultiple { get; } public abstract bool CanFixInProcedure { get; } public abstract bool CanFixInModule { get; } public abstract bool CanFixInProject { get; } - - public virtual bool CanFixAll => CanFixInProject; + public abstract bool CanFixAll { get; } } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Abstract/RefactoringQuickFixBase.cs b/Rubberduck.CodeAnalysis/QuickFixes/Abstract/RefactoringQuickFixBase.cs index 3106e14dc5..de31399737 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Abstract/RefactoringQuickFixBase.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Abstract/RefactoringQuickFixBase.cs @@ -39,8 +39,10 @@ public override void Fix(IInspectionResult result, IRewriteSession rewriteSessio protected abstract void Refactor(IInspectionResult result); + public override bool CanFixMultiple => false; public override bool CanFixInProcedure => false; public override bool CanFixInModule => false; public override bool CanFixInProject => false; + public override bool CanFixAll => false; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AccessSheetUsingCodeNameQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AccessSheetUsingCodeNameQuickFix.cs index 098690c113..39cc8cfc39 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AccessSheetUsingCodeNameQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AccessSheetUsingCodeNameQuickFix.cs @@ -17,7 +17,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddAttributeAnnotationQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddAttributeAnnotationQuickFix.cs index b9383e0c5d..3e3f4e95d2 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddAttributeAnnotationQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddAttributeAnnotationQuickFix.cs @@ -16,7 +16,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.AddAttributeAnnotationQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddIdentifierToWhiteListQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddIdentifierToWhiteListQuickFix.cs index 757ab37fe5..7ebdda68ea 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddIdentifierToWhiteListQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddIdentifierToWhiteListQuickFix.cs @@ -15,7 +15,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// internal sealed class AddIdentifierToWhiteListQuickFix : QuickFixBase { private readonly IConfigurationService _settings; @@ -38,8 +38,10 @@ public override void Fix(IInspectionResult result, IRewriteSession rewriteSessio public override string Description(IInspectionResult result) => Resources.Inspections.QuickFixes.WhiteListIdentifierQuickFix; - public override bool CanFixInProcedure { get; } = false; - public override bool CanFixInModule { get; } = false; - public override bool CanFixInProject { get; } = false; + public override bool CanFixMultiple => true; + public override bool CanFixInProcedure => false; + public override bool CanFixInModule => false; + public override bool CanFixInProject => false; + public override bool CanFixAll => false; } } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddMissingAttributeQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddMissingAttributeQuickFix.cs index 03ea7aef46..577d51751e 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddMissingAttributeQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddMissingAttributeQuickFix.cs @@ -15,7 +15,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// CodeKind.AttributesCode; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddStepOneQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddStepOneQuickFix.cs index 8a1c2af962..0d78b6c091 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddStepOneQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AddStepOneQuickFix.cs @@ -13,7 +13,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// true; public override bool CanFixInProcedure => true; - public override bool CanFixInModule => true; - public override bool CanFixInProject => true; + public override bool CanFixAll => true; public override string Description(IInspectionResult result) { diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AdjustAttributeAnnotationQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AdjustAttributeAnnotationQuickFix.cs index 439ef038b6..ed2c1785df 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AdjustAttributeAnnotationQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AdjustAttributeAnnotationQuickFix.cs @@ -16,7 +16,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.AdjustAttributeAnnotationQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AdjustAttributeValuesQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AdjustAttributeValuesQuickFix.cs index d5c37ea40d..2b90493cd7 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AdjustAttributeValuesQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AdjustAttributeValuesQuickFix.cs @@ -17,7 +17,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// CodeKind.AttributesCode; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ApplicationWorksheetFunctionQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ApplicationWorksheetFunctionQuickFix.cs index 0d5f89fc89..3fec3950e1 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ApplicationWorksheetFunctionQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ApplicationWorksheetFunctionQuickFix.cs @@ -11,7 +11,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.ApplicationWorksheetFunctionQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AssignedByValParameterMakeLocalCopyQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AssignedByValParameterMakeLocalCopyQuickFix.cs index 16e3ab90e0..2881f1e43b 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AssignedByValParameterMakeLocalCopyQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/AssignedByValParameterMakeLocalCopyQuickFix.cs @@ -23,7 +23,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.AssignedByValParameterMakeLocalCopyQuickFix; + public override bool CanFixMultiple => false; public override bool CanFixInProcedure => false; public override bool CanFixInModule => false; public override bool CanFixInProject => false; + public override bool CanFixAll => false; private string PromptForLocalVariableName(Declaration target) { diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ChangeDimToPrivateQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ChangeDimToPrivateQuickFix.cs index 333aa75a85..1faad3e00c 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ChangeDimToPrivateQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ChangeDimToPrivateQuickFix.cs @@ -12,7 +12,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.ChangeDimToPrivateQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ChangeIntegerToLongQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ChangeIntegerToLongQuickFix.cs index 9bc6ed1130..28fb822982 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ChangeIntegerToLongQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ChangeIntegerToLongQuickFix.cs @@ -19,7 +19,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.IntegerDataTypeQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; private static int GetParameterIndex(VBAParser.ArgContext context) { diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ChangeProcedureToFunctionQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ChangeProcedureToFunctionQuickFix.cs index b339fcd6e2..310f25be12 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ChangeProcedureToFunctionQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ChangeProcedureToFunctionQuickFix.cs @@ -17,7 +17,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => false; public override bool CanFixInProject => false; + public override bool CanFixAll => false; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ConvertToProcedureQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ConvertToProcedureQuickFix.cs index 3cc820a6a3..542e5a259d 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ConvertToProcedureQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ConvertToProcedureQuickFix.cs @@ -19,7 +19,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.ConvertFunctionToProcedureQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => true; public override bool CanFixInProject => false; + public override bool CanFixAll => false; private IEnumerable GetReturnStatements(Declaration declaration) { diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/DeclareAsExplicitVariantQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/DeclareAsExplicitVariantQuickFix.cs index 25a25134fd..88a527efcb 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/DeclareAsExplicitVariantQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/DeclareAsExplicitVariantQuickFix.cs @@ -13,7 +13,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.DeclareAsExplicitVariantQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ExpandBangNotationQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ExpandBangNotationQuickFix.cs index 4cb4aae0a2..69edb13884 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ExpandBangNotationQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ExpandBangNotationQuickFix.cs @@ -20,7 +20,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; private string NonIdentifierCharacters = "[](){}\r\n\t.,'\"\\ |!@#$%^&*-+:=; "; private string AdditionalNonFirstIdentifierCharacters = "0123456789_"; diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ExpandDefaultMemberQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ExpandDefaultMemberQuickFix.cs index 715f8d015e..07160ef591 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ExpandDefaultMemberQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ExpandDefaultMemberQuickFix.cs @@ -24,7 +24,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; private string NonIdentifierCharacters = "[](){}\r\n\t.,'\"\\ |!@#$%^&*-+:=; "; private string AdditionalNonFirstIdentifierCharacters = "0123456789_"; diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreInModuleQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreInModuleQuickFix.cs index 653e050fa3..4caa3d6ee4 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreInModuleQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreInModuleQuickFix.cs @@ -13,7 +13,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// Adds an '@IgnoreModule annotation to ignore a inspection results for a specific inspection inside a whole module. Applicable to all inspections whose results can be annotated in a module. /// - /// + /// /// /// /// true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => true; public override bool CanFixInProject => true; diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreOnceQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreOnceQuickFix.cs index 62515c434c..85816b75a4 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreOnceQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IgnoreOnceQuickFix.cs @@ -14,7 +14,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// Adds an '@Ignore annotation to ignore a specific inspection result. Applicable to all inspections whose results can be annotated in a module. /// - /// + /// /// /// /// true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IntroduceLocalVariableQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IntroduceLocalVariableQuickFix.cs index cdd91c76f1..41dbc9f9b7 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IntroduceLocalVariableQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IntroduceLocalVariableQuickFix.cs @@ -15,7 +15,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; public override void Fix(IInspectionResult result, IRewriteSession rewriteSession) { diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IsMissingOnInappropriateArgumentQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IsMissingOnInappropriateArgumentQuickFix.cs index 7e30e5ee8a..e530acb68a 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IsMissingOnInappropriateArgumentQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/IsMissingOnInappropriateArgumentQuickFix.cs @@ -18,7 +18,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.IsMissingOnInappropriateArgumentQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => false; public override bool CanFixInProject => false; + public override bool CanFixAll => false; } } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/MakeSingleLineParameterQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/MakeSingleLineParameterQuickFix.cs index 743fb2fee6..8d26c67450 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/MakeSingleLineParameterQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/MakeSingleLineParameterQuickFix.cs @@ -12,7 +12,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.MakeSingleLineParameterQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/OptionExplicitQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/OptionExplicitQuickFix.cs index 64fef76819..75d989534d 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/OptionExplicitQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/OptionExplicitQuickFix.cs @@ -13,7 +13,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.OptionExplicitQuickFix; - + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => false; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByReferenceQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByReferenceQuickFix.cs index 7473366e93..b050a6b96b 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByReferenceQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByReferenceQuickFix.cs @@ -12,7 +12,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.PassParameterByReferenceQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByValueQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByValueQuickFix.cs index 6059d909fd..a036e0e318 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByValueQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByValueQuickFix.cs @@ -16,7 +16,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/Refactoring/EncapsulateFieldQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/Refactoring/EncapsulateFieldQuickFix.cs index 00ef0e10c2..ab29447895 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/Refactoring/EncapsulateFieldQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/Refactoring/EncapsulateFieldQuickFix.cs @@ -11,7 +11,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete.Refactoring /// /// /// - /// + /// /// /// /// /// /// - /// + /// /// /// /// /// /// - /// + /// /// /// /// /// /// - /// + /// /// /// /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveAnnotationQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => false; public override bool CanFixInProject => false; + public override bool CanFixAll => false; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveAttributeQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveAttributeQuickFix.cs index 21cafd04d5..8f37c3f35c 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveAttributeQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveAttributeQuickFix.cs @@ -16,7 +16,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// CodeKind.AttributesCode; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => false; public override bool CanFixInProject => false; + public override bool CanFixAll => false; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveCommentQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveCommentQuickFix.cs index 1d0c3c49f1..41cf3346a4 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveCommentQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveCommentQuickFix.cs @@ -11,7 +11,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveCommentQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveDuplicatedAnnotationQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveDuplicatedAnnotationQuickFix.cs index 0e88d6f8a8..46e8735952 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveDuplicatedAnnotationQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveDuplicatedAnnotationQuickFix.cs @@ -14,7 +14,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveDuplicatedAnnotationQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveEmptyElseBlockQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveEmptyElseBlockQuickFix.cs index f132120675..166c941705 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveEmptyElseBlockQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveEmptyElseBlockQuickFix.cs @@ -12,7 +12,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveEmptyElseBlockQuickFix; + public override bool CanFixMultiple => false; public override bool CanFixInProcedure => false; public override bool CanFixInModule => false; public override bool CanFixInProject => false; + public override bool CanFixAll => false; } } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveEmptyIfBlockQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveEmptyIfBlockQuickFix.cs index 93cda95640..b5bf7a2ebc 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveEmptyIfBlockQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveEmptyIfBlockQuickFix.cs @@ -16,7 +16,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveEmptyIfBlockQuickFix; + public override bool CanFixMultiple => false; public override bool CanFixInProcedure => false; public override bool CanFixInModule => false; public override bool CanFixInProject => false; + public override bool CanFixAll => false; } } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveExplicitByRefModifierQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveExplicitByRefModifierQuickFix.cs index 3e4320a094..0863a64766 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveExplicitByRefModifierQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveExplicitByRefModifierQuickFix.cs @@ -17,7 +17,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RedundantByRefModifierQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; private static int ParameterIndex(ParameterDeclaration parameter, IParameterizedDeclaration enclosingMember) { diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveExplicitCallStatementQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveExplicitCallStatementQuickFix.cs index c39c013767..2e8a3b4742 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveExplicitCallStatementQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveExplicitCallStatementQuickFix.cs @@ -12,7 +12,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveObsoleteStatementQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveExplicitLetStatementQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveExplicitLetStatementQuickFix.cs index 33a9f629a3..cc2feae70b 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveExplicitLetStatementQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveExplicitLetStatementQuickFix.cs @@ -13,7 +13,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveObsoleteStatementQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveLocalErrorQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveLocalErrorQuickFix.cs index 35ee1e01cc..cd5a39eca2 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveLocalErrorQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveLocalErrorQuickFix.cs @@ -12,7 +12,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveLocalErrorQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveOptionBaseStatementQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveOptionBaseStatementQuickFix.cs index 7a00523199..2538a3669f 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveOptionBaseStatementQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveOptionBaseStatementQuickFix.cs @@ -11,7 +11,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveOptionBaseStatementQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => false; public override bool CanFixInProject => false; + public override bool CanFixAll => false; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveStepOneQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveStepOneQuickFix.cs index da135cb793..fd79e030d7 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveStepOneQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveStepOneQuickFix.cs @@ -11,7 +11,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// true; public override bool CanFixInProcedure => true; - public override bool CanFixInModule => true; - public override bool CanFixInProject => true; + public override bool CanFixAll => true; public override string Description(IInspectionResult result) => Resources.Inspections.QuickFixes.RemoveStepOneQuickFix; diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveStopKeywordQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveStopKeywordQuickFix.cs index cfdf2ac66a..7445930fe2 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveStopKeywordQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveStopKeywordQuickFix.cs @@ -11,7 +11,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveStopKeywordQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => false; public override bool CanFixInProject => false; + public override bool CanFixAll => false; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveTypeHintsQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveTypeHintsQuickFix.cs index 268e2d370c..44561de5d7 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveTypeHintsQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveTypeHintsQuickFix.cs @@ -14,7 +14,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveTypeHintsQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnassignedIdentifierQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnassignedIdentifierQuickFix.cs index 58cede4288..99332eca24 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnassignedIdentifierQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnassignedIdentifierQuickFix.cs @@ -11,7 +11,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveUnassignedIdentifierQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnassignedVariableUsageQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnassignedVariableUsageQuickFix.cs index 03c63521c9..b6a7a1979c 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnassignedVariableUsageQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnassignedVariableUsageQuickFix.cs @@ -17,7 +17,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveUnassignedVariableUsageQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnusedDeclarationQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnusedDeclarationQuickFix.cs index f90a4752aa..2116c8d0ac 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnusedDeclarationQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnusedDeclarationQuickFix.cs @@ -14,7 +14,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveUnusedDeclarationQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceEmptyStringLiteralStatementQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceEmptyStringLiteralStatementQuickFix.cs index 775bbb5022..2ccfd7e086 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceEmptyStringLiteralStatementQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceEmptyStringLiteralStatementQuickFix.cs @@ -11,7 +11,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.EmptyStringLiteralInspectionQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceGlobalModifierQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceGlobalModifierQuickFix.cs index 07194dbee3..b3f2cfe1d2 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceGlobalModifierQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceGlobalModifierQuickFix.cs @@ -14,7 +14,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.ObsoleteGlobalInspectionQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceIfElseWithConditionalStatementQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceIfElseWithConditionalStatementQuickFix.cs index 1f18bedbac..294d4ca64b 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceIfElseWithConditionalStatementQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceIfElseWithConditionalStatementQuickFix.cs @@ -13,7 +13,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.ReplaceIfElseWithConditionalStatementQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceObsoleteCommentMarkerQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceObsoleteCommentMarkerQuickFix.cs index 5eb436f402..54044f738b 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceObsoleteCommentMarkerQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceObsoleteCommentMarkerQuickFix.cs @@ -12,7 +12,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.RemoveObsoleteStatementQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceObsoleteErrorStatementQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceObsoleteErrorStatementQuickFix.cs index 350b667196..3bdd42380f 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceObsoleteErrorStatementQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceObsoleteErrorStatementQuickFix.cs @@ -12,7 +12,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.ReplaceObsoleteErrorStatementQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceWhileWendWithDoWhileLoopQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceWhileWendWithDoWhileLoopQuickFix.cs index 11de59af5f..043205bcf2 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceWhileWendWithDoWhileLoopQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/ReplaceWhileWendWithDoWhileLoopQuickFix.cs @@ -12,7 +12,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.ReplaceWhileWendWithDoWhileLoopQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RestoreErrorHandlingQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RestoreErrorHandlingQuickFix.cs index a3daa8b203..bb9dbf86d7 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RestoreErrorHandlingQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RestoreErrorHandlingQuickFix.cs @@ -16,7 +16,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.UnhandledOnErrorResumeNextInspectionQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; private static int GetMaximumExistingLabelIndex(IEnumerable labelContexts) { diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SetExplicitVariantReturnTypeQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SetExplicitVariantReturnTypeQuickFix.cs index 0205194081..15e888231d 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SetExplicitVariantReturnTypeQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SetExplicitVariantReturnTypeQuickFix.cs @@ -13,7 +13,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.SetExplicitVariantReturnTypeQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SpecifyExplicitByRefModifierQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SpecifyExplicitByRefModifierQuickFix.cs index d5668cee3c..747668a9a2 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SpecifyExplicitByRefModifierQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SpecifyExplicitByRefModifierQuickFix.cs @@ -17,7 +17,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.ImplicitByRefModifierQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; private static int ParameterIndex(ParameterDeclaration parameter, IParameterizedDeclaration enclosingMember) { diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SpecifyExplicitPublicModifierQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SpecifyExplicitPublicModifierQuickFix.cs index d38d92fea5..f1663115e6 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SpecifyExplicitPublicModifierQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SpecifyExplicitPublicModifierQuickFix.cs @@ -11,7 +11,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.SpecifyExplicitPublicModifierQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SplitMultipleDeclarationsQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SplitMultipleDeclarationsQuickFix.cs index 4af8dbe637..80560ce889 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SplitMultipleDeclarationsQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/SplitMultipleDeclarationsQuickFix.cs @@ -15,7 +15,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.SplitMultipleDeclarationsQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/UntypedFunctionUsageQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/UntypedFunctionUsageQuickFix.cs index bf54b531f0..5338f547e6 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/UntypedFunctionUsageQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/UntypedFunctionUsageQuickFix.cs @@ -15,7 +15,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/UseSetKeywordForObjectAssignmentQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/UseSetKeywordForObjectAssignmentQuickFix.cs index f66da26ab1..16eaf7a0dd 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/UseSetKeywordForObjectAssignmentQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/UseSetKeywordForObjectAssignmentQuickFix.cs @@ -14,7 +14,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.SetObjectVariableQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => true; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; } } \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/WriteOnlyPropertyQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/WriteOnlyPropertyQuickFix.cs index c05c3000b4..3f3b637e77 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/WriteOnlyPropertyQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/WriteOnlyPropertyQuickFix.cs @@ -15,7 +15,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// /// /// - /// + /// /// /// /// Resources.Inspections.QuickFixes.WriteOnlyPropertyQuickFix; + public override bool CanFixMultiple => true; public override bool CanFixInProcedure => false; public override bool CanFixInModule => true; public override bool CanFixInProject => true; + public override bool CanFixAll => true; private string GetParamText(ParameterDeclaration param) { diff --git a/Rubberduck.CodeAnalysis/QuickFixes/IQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/IQuickFix.cs index 7f07f4189a..f533cf473d 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/IQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/IQuickFix.cs @@ -11,6 +11,7 @@ public interface IQuickFix void Fix(IInspectionResult result, IRewriteSession rewriteSession); string Description(IInspectionResult result); + bool CanFixMultiple { get; } bool CanFixInProcedure { get; } bool CanFixInModule { get; } bool CanFixInProject { get; } From 1b3e259f6b730ffcd660a9c68b85dbe73437f626 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Sat, 27 Jun 2020 02:34:20 +0200 Subject: [PATCH 63/82] Introduce option to fix all selected occurrences --- .../Inspections/InspectionResultsControl.xaml | 10 +- .../Inspections/InspectionResultsViewModel.cs | 109 ++++++++++++++---- ...ickFixCommandParametersToTupleConverter.cs | 27 +++++ .../Inspections/InspectionsUI.Designer.cs | 9 ++ .../Inspections/InspectionsUI.de.resx | 5 +- .../Inspections/InspectionsUI.resx | 3 + 6 files changed, 139 insertions(+), 24 deletions(-) create mode 100644 Rubberduck.Core/UI/Inspections/QuickFixCommandParametersToTupleConverter.cs diff --git a/Rubberduck.Core/UI/Inspections/InspectionResultsControl.xaml b/Rubberduck.Core/UI/Inspections/InspectionResultsControl.xaml index 4b7cda2c30..99d732a0fa 100644 --- a/Rubberduck.Core/UI/Inspections/InspectionResultsControl.xaml +++ b/Rubberduck.Core/UI/Inspections/InspectionResultsControl.xaml @@ -22,11 +22,19 @@ +