diff --git a/README.md b/README.md index aac07a5f0f..52ab78c6df 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![banner](https://user-images.githubusercontent.com/5751684/113501222-8edfe880-94f1-11eb-99a9-64583e413ef3.png) + [**Installing**](https://github.com/rubberduck-vba/Rubberduck/wiki/Installing) • [Contributing](https://github.com/rubberduck-vba/Rubberduck/blob/next/CONTRIBUTING.md) • [Attributions](https://github.com/rubberduck-vba/Rubberduck/blob/next/docs/Attributions.md) • [Blog](https://rubberduckvba.blog) • [Wiki](https://github.com/rubberduck-vba/Rubberduck/wiki) • [rubberduckvba.com](https://rubberduckvba.com) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/EmptyMethodInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/EmptyMethodInspection.cs index 4d8f0d4bbe..baaf3d6688 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/EmptyMethodInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/EmptyMethodInspection.cs @@ -33,7 +33,7 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete /// ]]> /// /// - internal class EmptyMethodInspection : DeclarationInspectionBase + internal sealed class EmptyMethodInspection : DeclarationInspectionBase { public EmptyMethodInspection(IDeclarationFinderProvider declarationFinderProvider) : base(declarationFinderProvider, DeclarationType.Member) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/EmptyModuleInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/EmptyModuleInspection.cs index 1350acff4b..fc99597114 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/EmptyModuleInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/EmptyModuleInspection.cs @@ -84,7 +84,7 @@ public override bool VisitModuleDeclarations(VBAParser.ModuleDeclarationsContext public override bool VisitModuleDeclarationsElement(VBAParser.ModuleDeclarationsElementContext context) { return context.moduleVariableStmt() == null - && context.constStmt() == null + && context.moduleConstStmt() == null && context.enumerationStmt() == null && context.udtDeclaration() == null && context.eventStmt() == null diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureCanBeWrittenAsFunctionInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureCanBeWrittenAsFunctionInspection.cs index fcb4f63c7b..24ebd6735d 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureCanBeWrittenAsFunctionInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureCanBeWrittenAsFunctionInspection.cs @@ -13,8 +13,8 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete /// Warns about 'Sub' procedures that could be refactored into a 'Function'. /// /// - /// Idiomatic VB code uses 'Function' procedures to return a single value. If the procedure isn't side-effecting, consider writing is as a - /// 'Function' rather than a 'Sub' the returns a result through a 'ByRef' parameter. + /// Idiomatic VB code uses 'Function' procedures to return a single value. If the procedure isn't side-effecting, consider writing it as a + /// 'Function' rather than a 'Sub' that returns a result through a 'ByRef' parameter. /// /// /// diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/PublicEnumerationDeclaredInWorksheetInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/PublicEnumerationDeclaredInWorksheetInspection.cs new file mode 100644 index 0000000000..ac5bbe60e4 --- /dev/null +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/PublicEnumerationDeclaredInWorksheetInspection.cs @@ -0,0 +1,83 @@ +using Rubberduck.CodeAnalysis.Inspections.Abstract; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Parsing.VBA.DeclarationCaching; +using Rubberduck.Resources.Inspections; +using Rubberduck.VBEditor.SafeComWrappers; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.CodeAnalysis.Inspections.Concrete +{ + /// + /// Identifies public enumerations declared within worksheet modules. + /// + /// + /// Copying a worksheet which contains a public Enum declaration will also create a copy of the Enum declaration. + /// The copied Enum declaration will result in an 'Ambiguous name detected' compiler error. + /// Declaring Enumerations in Standard or Class modules avoids unintentional duplication of an Enum declaration. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal sealed class PublicEnumerationDeclaredInWorksheetInspection : DeclarationInspectionBase + { + private readonly string[] _worksheetSuperTypeNames = new string[] { "Worksheet", "_Worksheet" }; + + public PublicEnumerationDeclaredInWorksheetInspection(IDeclarationFinderProvider declarationFinderProvider) + : base(declarationFinderProvider, DeclarationType.Enumeration) + {} + + protected override bool IsResultDeclaration(Declaration enumeration, DeclarationFinder finder) + { + if (enumeration.Accessibility != Accessibility.Private + && enumeration.QualifiedModuleName.ComponentType == ComponentType.Document) + { + if (enumeration.ParentDeclaration is ClassModuleDeclaration classModuleDeclaration) + { + return RetrieveSuperTypeNames(classModuleDeclaration).Intersect(_worksheetSuperTypeNames).Any(); + } + } + + return false; + } + + protected override string ResultDescription(Declaration declaration) + { + return string.Format(InspectionResults.PublicEnumerationDeclaredInWorksheetInspection, + declaration.IdentifierName); + } + + /// + /// Supports property injection for testing. + /// + /// + /// MockParser does not populate SuperTypes/SuperTypeNames. RetrieveSuperTypeNames Func allows injection + /// of ClassModuleDeclaration.SuperTypeNames property results. + /// + public Func> RetrieveSuperTypeNames { set; private get; } = GetSuperTypeNames; + + private static IEnumerable GetSuperTypeNames(ClassModuleDeclaration classModule) + { + return classModule.SupertypeNames; + } + } +} diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/SheetAccessedUsingStringInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/SheetAccessedUsingStringInspection.cs index e4db4f955b..a1e98f2cfa 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/SheetAccessedUsingStringInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/SheetAccessedUsingStringInspection.cs @@ -229,9 +229,6 @@ private static string ComponentPropertyValue(IVBComponent component, string prop return null; } - protected override string ResultDescription(IdentifierReference reference, string codeName) - { - return InspectionResults.SheetAccessedUsingStringInspection; - } + protected override string ResultDescription(IdentifierReference reference, string codeName) => InspectionResults.SheetAccessedUsingStringInspection; } } diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/KeywordsUsedAsMemberInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/KeywordsUsedAsMemberInspection.cs index 6a5a946820..b80fb257e8 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/KeywordsUsedAsMemberInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/KeywordsUsedAsMemberInspection.cs @@ -19,7 +19,7 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete.ThunderCode /// While perfectly legal as Type or Enum member names, these identifiers should be avoided: /// they need to be square-bracketed everywhere they are used. /// - internal class KeywordsUsedAsMemberInspection : DeclarationInspectionBase + internal sealed class KeywordsUsedAsMemberInspection : DeclarationInspectionBase { public KeywordsUsedAsMemberInspection(IDeclarationFinderProvider declarationFinderProvider) : base(declarationFinderProvider, DeclarationType.EnumerationMember, DeclarationType.UserDefinedTypeMember) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/NonBreakingSpaceIdentifierInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/NonBreakingSpaceIdentifierInspection.cs index aa9e8bf798..4987df727c 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/NonBreakingSpaceIdentifierInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/NonBreakingSpaceIdentifierInspection.cs @@ -14,7 +14,7 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete.ThunderCode /// code our friend Andrew Jackson would have written to confuse Rubberduck's parser and/or resolver. /// This inspection may accidentally reveal non-breaking spaces in code copied and pasted from a website. /// - internal class NonBreakingSpaceIdentifierInspection : DeclarationInspectionBase + internal sealed class NonBreakingSpaceIdentifierInspection : DeclarationInspectionBase { private const string Nbsp = "\u00A0"; diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/UDTMemberNotUsedInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/UDTMemberNotUsedInspection.cs new file mode 100644 index 0000000000..9857359cdd --- /dev/null +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/UDTMemberNotUsedInspection.cs @@ -0,0 +1,83 @@ +using Rubberduck.CodeAnalysis.Inspections.Abstract; +using Rubberduck.CodeAnalysis.Inspections.Extensions; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Parsing.VBA.DeclarationCaching; +using Rubberduck.Resources.Inspections; +using System.Linq; + +namespace Rubberduck.CodeAnalysis.Inspections.Concrete +{ + /// + /// Warns about User Defined Type (UDT) members that are never referenced. + /// + /// + /// Declarations that are never used should be removed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal sealed class UDTMemberNotUsedInspection : DeclarationInspectionBase + { + public UDTMemberNotUsedInspection(IDeclarationFinderProvider declarationFinderProvider) + : base(declarationFinderProvider, DeclarationType.UserDefinedTypeMember) + {} + + protected override bool IsResultDeclaration(Declaration declaration, DeclarationFinder finder) + { + return declaration.DeclarationType.Equals(DeclarationType.UserDefinedTypeMember) + && !declaration.References.Any(); + } + + protected override string ResultDescription(Declaration declaration) + { + var declarationType = declaration.DeclarationType.ToLocalizedString(); + var declarationName = declaration.IdentifierName; + return string.Format( + InspectionResults.IdentifierNotUsedInspection, + declarationType, + declarationName); + } + } +} diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Abstract/QuickFixBase.cs b/Rubberduck.CodeAnalysis/QuickFixes/Abstract/QuickFixBase.cs index 10545fb184..851cc390bc 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Abstract/QuickFixBase.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Abstract/QuickFixBase.cs @@ -55,6 +55,22 @@ public void RemoveInspections(params Type[] inspections) public virtual CodeKind TargetCodeKind => CodeKind.CodePaneCode; public abstract void Fix(IInspectionResult result, IRewriteSession rewriteSession); + + /// + /// FixMany defers the enumeration of inspection results to the QuickFix + /// + /// + /// The default implementation enumerates the results collection calling Fix() for each result. + /// Override this funcion when a QuickFix needs operate on results as a group (e.g., RemoveUnusedDeclarationQuickFix) + /// + public virtual void Fix(IReadOnlyCollection results, IRewriteSession rewriteSession) + { + foreach (var result in results) + { + Fix(result, rewriteSession); + } + } + public abstract string Description(IInspectionResult result); public abstract bool CanFixMultiple { get; } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveOptionBaseStatementQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveRedundantOptionStatementQuickFix.cs similarity index 85% rename from Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveOptionBaseStatementQuickFix.cs rename to Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveRedundantOptionStatementQuickFix.cs index 2538a3669f..c153475574 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveOptionBaseStatementQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveRedundantOptionStatementQuickFix.cs @@ -35,9 +35,9 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// ]]> /// /// - internal sealed class RemoveOptionBaseStatementQuickFix : QuickFixBase + internal sealed class RemoveRedundantOptionStatementQuickFix : QuickFixBase { - public RemoveOptionBaseStatementQuickFix() + public RemoveRedundantOptionStatementQuickFix() : base(typeof(RedundantOptionInspection)) {} @@ -47,7 +47,12 @@ public override void Fix(IInspectionResult result, IRewriteSession rewriteSessio rewriter.Remove(result.Context); } - public override string Description(IInspectionResult result) => Resources.Inspections.QuickFixes.RemoveOptionBaseStatementQuickFix; + public override string Description(IInspectionResult result) + { + return string.Format( + Resources.Inspections.QuickFixes.RemoveRedundantOptionStatementQuickFix, + result.Context.GetText()); + } public override bool CanFixMultiple => true; public override bool CanFixInProcedure => false; diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnassignedIdentifierQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnassignedIdentifierQuickFix.cs index 99332eca24..ac24979f85 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnassignedIdentifierQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnassignedIdentifierQuickFix.cs @@ -2,6 +2,10 @@ using Rubberduck.CodeAnalysis.Inspections.Concrete; using Rubberduck.CodeAnalysis.QuickFixes.Abstract; using Rubberduck.Parsing.Rewriter; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.DeleteDeclarations; +using System.Collections.Generic; +using System.Linq; namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete { @@ -35,14 +39,25 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// internal sealed class RemoveUnassignedIdentifierQuickFix : QuickFixBase { - public RemoveUnassignedIdentifierQuickFix() + private readonly ICodeOnlyRefactoringAction _refactoring; + public RemoveUnassignedIdentifierQuickFix(DeleteDeclarationsRefactoringAction refactoringAction) : base(typeof(VariableNotAssignedInspection)) - {} + { + _refactoring = refactoringAction; + } public override void Fix(IInspectionResult result, IRewriteSession rewriteSession) { - var rewriter = rewriteSession.CheckOutModuleRewriter(result.Target.QualifiedModuleName); - rewriter.Remove(result.Target); + var model = new DeleteDeclarationsModel(result.Target); + + _refactoring.Refactor(model, rewriteSession); + } + + public override void Fix(IReadOnlyCollection results, IRewriteSession rewriteSession) + { + var model = new DeleteDeclarationsModel(results.Select(r => r.Target)); + + _refactoring.Refactor(model, rewriteSession); } public override string Description(IInspectionResult result) => Resources.Inspections.QuickFixes.RemoveUnassignedIdentifierQuickFix; diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnusedDeclarationQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnusedDeclarationQuickFix.cs index 2116c8d0ac..8bd00953db 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnusedDeclarationQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/RemoveUnusedDeclarationQuickFix.cs @@ -2,6 +2,12 @@ using Rubberduck.CodeAnalysis.Inspections.Concrete; using Rubberduck.CodeAnalysis.QuickFixes.Abstract; using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.Common; +using Rubberduck.Refactorings.DeleteDeclarations; +using System.Collections.Generic; +using System.Linq; namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete { @@ -31,7 +37,6 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// Option Explicit /// /// Public Sub DoSomething() - /// /// Debug.Print 42 /// End Sub /// ]]> @@ -39,17 +44,30 @@ namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete /// internal sealed class RemoveUnusedDeclarationQuickFix : QuickFixBase { - public RemoveUnusedDeclarationQuickFix() + private readonly ICodeOnlyRefactoringAction _refactoring; + + public RemoveUnusedDeclarationQuickFix(DeleteDeclarationsRefactoringAction refactoringAction) : base(typeof(ConstantNotUsedInspection), typeof(ProcedureNotUsedInspection), typeof(VariableNotUsedInspection), - typeof(LineLabelNotUsedInspection)) - {} + typeof(LineLabelNotUsedInspection), + typeof(UDTMemberNotUsedInspection)) + { + _refactoring = refactoringAction; + } public override void Fix(IInspectionResult result, IRewriteSession rewriteSession) { - var rewriter = rewriteSession.CheckOutModuleRewriter(result.Target.QualifiedModuleName); - rewriter.Remove(result.Target); + var model = new DeleteDeclarationsModel(result.Target); + + _refactoring.Refactor(model, rewriteSession); + } + + public override void Fix(IReadOnlyCollection results, IRewriteSession rewriteSession) + { + var model = new DeleteDeclarationsModel(results.Select(r => r.Target)); + + _refactoring.Refactor(model, rewriteSession); } public override string Description(IInspectionResult result) => Resources.Inspections.QuickFixes.RemoveUnusedDeclarationQuickFix; diff --git a/Rubberduck.CodeAnalysis/QuickFixes/IQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/IQuickFix.cs index f533cf473d..3de80809f8 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/IQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/IQuickFix.cs @@ -9,6 +9,7 @@ namespace Rubberduck.CodeAnalysis.QuickFixes public interface IQuickFix { void Fix(IInspectionResult result, IRewriteSession rewriteSession); + void Fix(IReadOnlyCollection results, IRewriteSession rewriteSession); string Description(IInspectionResult result); bool CanFixMultiple { get; } diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Logistics/QuickFixProvider.cs b/Rubberduck.CodeAnalysis/QuickFixes/Logistics/QuickFixProvider.cs index 308a1bbf9f..48835f7c3c 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Logistics/QuickFixProvider.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Logistics/QuickFixProvider.cs @@ -59,17 +59,17 @@ public bool CanFix(IQuickFix fix, IInspectionResult result) && !result.DisabledQuickFixes.Contains(fix.GetType().Name); } - public void Fix(IQuickFix fix, IInspectionResult result) + public void Fix(IQuickFix quickFix, IInspectionResult result) { - if (!CanFix(fix, result)) + if (!CanFix(quickFix, result)) { return; } - var rewriteSession = RewriteSession(fix.TargetCodeKind); + var rewriteSession = RewriteSession(quickFix.TargetCodeKind); try { - fix.Fix(result, rewriteSession); + quickFix.Fix(result, rewriteSession); } catch (RewriteFailedException) { @@ -78,24 +78,24 @@ public void Fix(IQuickFix fix, IInspectionResult result) Apply(rewriteSession); } - public void Fix(IQuickFix fix, IEnumerable resultsToFix) + public void Fix(IQuickFix quickFix, IEnumerable resultsToFix) { - var results = resultsToFix.ToList(); + var fixableResults = resultsToFix.Where(r => CanFix(quickFix, r)).ToList(); - if (!results.Any()) + if (!fixableResults.Any()) { return; } - var rewriteSession = RewriteSession(fix.TargetCodeKind); - foreach (var result in results) - { - if (!CanFix(fix, result)) - { - continue; - } + var rewriteSession = RewriteSession(quickFix.TargetCodeKind); - fix.Fix(result, rewriteSession); + try + { + quickFix.Fix(fixableResults, rewriteSession); + } + catch (RewriteFailedException) + { + _failureNotifier.NotifyQuickFixExecutionFailure(rewriteSession.Status); } Apply(rewriteSession); } diff --git a/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesUI.it.resx b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesUI.it.resx index 5d63958833..6972611447 100644 --- a/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesUI.it.resx +++ b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesUI.it.resx @@ -131,6 +131,7 @@ Database di Microsoft Access({0})|{0} + {0} = elenco delle estensioni delimitato da punto e virgola nel formato *.ext Controlli ActiveX (*.ocx)|*.ocx @@ -140,33 +141,40 @@ File di Microsoft Excel ({0})|{0} + {0} = elenco delle estensioni delimitato da punto e virgola nel formato *.ext File eseguibili (*.exe;*.dll)|*.exe;*.dll File VBA di Outlook ({0})|{0} + {0} = elenco delle estensioni delimitato da punto e virgola nel formato *.ext File Addin di PowerPoint({0})|{0} + {0} = elenco delle estensioni delimitato da punto e virgola nel formato *.ext File di Publisher ({0}|{0} + {0} = elenco delle estensioni delimitato da punto e virgola nel formato *.ext Tipi Libreria (*.olb;*.tlb;*.dll)|*.olb;*.tlb;*.dll Tutti i file di Visio ({0})|{0} + {0} = elenco delle estensioni delimitato da punto e virgola nel formato *.ext Documenti di Word ({0})|{0} + {0} = elenco delle estensioni delimitato da punto e virgola nel formato *.ext Aggiungi/Rimuovi Riferimento... Aggiungi/Rimuovi Riferimenti - {0} + {0} = Nome del progetto Standard diff --git a/Rubberduck.Core/UI/Command/ComCommands/ExportAllCommand.cs b/Rubberduck.Core/UI/Command/ComCommands/ExportAllCommand.cs index 6d0e2adc4d..01ba967f84 100644 --- a/Rubberduck.Core/UI/Command/ComCommands/ExportAllCommand.cs +++ b/Rubberduck.Core/UI/Command/ComCommands/ExportAllCommand.cs @@ -1,4 +1,5 @@ using Path = System.IO.Path; +using Directory = System.IO.Directory; using System.Windows.Forms; using Rubberduck.Navigation.CodeExplorer; using Rubberduck.Resources; @@ -6,25 +7,29 @@ using Rubberduck.VBEditor.Events; using Rubberduck.VBEditor.SafeComWrappers; using Rubberduck.VBEditor.SafeComWrappers.Abstract; +using System.Collections.Generic; namespace Rubberduck.UI.Command.ComCommands { public class ExportAllCommand : ComCommandBase { private readonly IVBE _vbe; - private readonly IFileSystemBrowserFactory _factory; private readonly IProjectsProvider _projectsProvider; + private readonly IFileSystemBrowserFactory _factory; + private readonly ProjectToExportFolderMap _projectToExportFolderMap; public ExportAllCommand( IVBE vbe, IFileSystemBrowserFactory folderBrowserFactory, IVbeEvents vbeEvents, - IProjectsProvider projectsProvider) + IProjectsProvider projectsProvider, + ProjectToExportFolderMap projectToExportFolderMap) : base(vbeEvents) { _vbe = vbe; _factory = folderBrowserFactory; _projectsProvider = projectsProvider; + _projectToExportFolderMap = projectToExportFolderMap; AddToCanExecuteEvaluation(SpecialEvaluateCanExecute); } @@ -103,23 +108,62 @@ protected override void OnExecute(object parameter) private void Export(IVBProject project) { - var desc = string.Format(RubberduckUI.ExportAllCommand_SaveAsDialog_Title, project.Name); + var initialFolderBrowserPath = GetInitialFolderBrowserPath(project); - // If .GetDirectoryName is passed an empty string for a RootFolder, - // it defaults to the Documents library (Win 7+) or equivalent. - var path = string.IsNullOrWhiteSpace(project.FileName) - ? string.Empty - : Path.GetDirectoryName(project.FileName); + var desc = string.Format(RubberduckUI.ExportAllCommand_SaveAsDialog_Title, project.Name); - using (var _folderBrowser = _factory.CreateFolderBrowser(desc, true, path)) + using (var _folderBrowser = _factory.CreateFolderBrowser(desc, true, initialFolderBrowserPath)) { var result = _folderBrowser.ShowDialog(); if (result == DialogResult.OK) { + _projectToExportFolderMap.AssignProjectExportFolder(project, _folderBrowser.SelectedPath); project.ExportSourceFiles(_folderBrowser.SelectedPath); } } } + + //protected scope to support testing + protected string GetInitialFolderBrowserPath(IVBProject project) + { + if (_projectToExportFolderMap.TryGetExportPathForProject(project, out string initialFolderBrowserPath)) + { + if (FolderExists(initialFolderBrowserPath)) + { + //Return the cached folderpath of the previous ExportAllCommand process + return initialFolderBrowserPath; + } + + //The folder used in the previous ExportAllComand process no longer exists, remove the cached folderpath + _projectToExportFolderMap.RemoveProject(project); + } + + //The folder of the workbook, or an empty string + initialFolderBrowserPath = GetDefaultExportFolder(project.FileName); + + if (!string.IsNullOrEmpty(initialFolderBrowserPath)) + { + _projectToExportFolderMap.AssignProjectExportFolder(project, initialFolderBrowserPath); + } + + return initialFolderBrowserPath; + } + + //protected scope to support testing + protected string GetDefaultExportFolder(string projectFileName) + { + // If .GetDirectoryName is passed an empty string for a RootFolder, + // it defaults to the Documents library (Win 7+) or equivalent. + return string.IsNullOrWhiteSpace(projectFileName) + ? string.Empty + : Path.GetDirectoryName(projectFileName); + } + + //protected virtual to support testing + protected virtual bool FolderExists(string path) + { + return Directory.Exists(path); + } } } \ No newline at end of file diff --git a/Rubberduck.Core/UI/ProjectToExportFolderMap.cs b/Rubberduck.Core/UI/ProjectToExportFolderMap.cs new file mode 100644 index 0000000000..60fc5c808b --- /dev/null +++ b/Rubberduck.Core/UI/ProjectToExportFolderMap.cs @@ -0,0 +1,51 @@ +using Rubberduck.VBEditor.SafeComWrappers.Abstract; +using System.Collections.Generic; + +namespace Rubberduck.UI +{ + /// + /// ProjectToExportFolderMap is a singleton container of Project/Folder pairs to + /// support multiple instances of the ExportAllCommand class + /// + public class ProjectToExportFolderMap + { + private readonly Dictionary _projectToExportFolderMap; + + public ProjectToExportFolderMap() + { + _projectToExportFolderMap = new Dictionary(); + } + + public void AssignProjectExportFolder(IVBProject project, string exportFolderpath) + { + if (project is null || string.IsNullOrWhiteSpace(exportFolderpath)) + { + return; + } + + if (!_projectToExportFolderMap.ContainsKey(project.FileName)) + { + _projectToExportFolderMap.Add(project.FileName, exportFolderpath); + return; + } + + _projectToExportFolderMap[project.FileName] = exportFolderpath; + } + + public bool TryGetExportPathForProject(IVBProject project, out string exportFolderpath) + { + exportFolderpath = string.Empty; + if (string.IsNullOrWhiteSpace(project.FileName)) + { + return false; + } + + return _projectToExportFolderMap.TryGetValue(project.FileName, out exportFolderpath); + } + + public void RemoveProject(IVBProject project) + { + _projectToExportFolderMap.Remove(project.FileName); + } + } +} diff --git a/Rubberduck.Core/UI/Refactorings/MoveCloserToUsage/MoveCloserToUsagePresenter.cs b/Rubberduck.Core/UI/Refactorings/MoveCloserToUsage/MoveCloserToUsagePresenter.cs new file mode 100644 index 0000000000..cebcd0f43c --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/MoveCloserToUsage/MoveCloserToUsagePresenter.cs @@ -0,0 +1,34 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.MoveCloserToUsage; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Rubberduck.UI.Refactorings.MoveCloserToUsage +{ + class MoveCloserToUsagePresenter : RefactoringPresenterBase, IMoveCloserToUsagePresenter + { + + private static readonly DialogData DialogData = DialogData.Create("RefactoringsUI.MoveCloserToUsageDialog_Caption", 164, 684); + + public MoveCloserToUsagePresenter(MoveCloserToUsageModel model, IRefactoringDialogFactory dialogFactory) : + base(DialogData, model, dialogFactory) + { + } + + public MoveCloserToUsageModel Show(VariableDeclaration target) + { + if (null == target) + { + return null; + } + + Model.Target = target; + + return Show(); + } + } +} diff --git a/Rubberduck.Core/UI/Refactorings/MoveCloserToUsage/MoveCloserToUsageView.xaml b/Rubberduck.Core/UI/Refactorings/MoveCloserToUsage/MoveCloserToUsageView.xaml new file mode 100644 index 0000000000..f3c247d86b --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/MoveCloserToUsage/MoveCloserToUsageView.xaml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rubberduck.Core/UI/Refactorings/MoveCloserToUsage/MoveCloserToUsageView.xaml.cs b/Rubberduck.Core/UI/Refactorings/MoveCloserToUsage/MoveCloserToUsageView.xaml.cs new file mode 100644 index 0000000000..f897818bf1 --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/MoveCloserToUsage/MoveCloserToUsageView.xaml.cs @@ -0,0 +1,31 @@ +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.MoveCloserToUsage; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Rubberduck.UI.Refactorings.MoveCloserToUsage +{ + /// + /// Interaktionslogik für MoveCloserToUsageView.xaml + /// + public partial class MoveCloserToUsageView : IRefactoringView + { + public MoveCloserToUsageView() + { + InitializeComponent(); + } + + } +} diff --git a/Rubberduck.Core/UI/Refactorings/MoveCloserToUsage/MoveCloserToUsageViewModel.cs b/Rubberduck.Core/UI/Refactorings/MoveCloserToUsage/MoveCloserToUsageViewModel.cs new file mode 100644 index 0000000000..52cbd62659 --- /dev/null +++ b/Rubberduck.Core/UI/Refactorings/MoveCloserToUsage/MoveCloserToUsageViewModel.cs @@ -0,0 +1,48 @@ +using NLog; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.MoveCloserToUsage; +using Rubberduck.UI.Command; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Rubberduck.UI.Refactorings.MoveCloserToUsage +{ + class MoveCloserToUsageViewModel : RefactoringViewModelBase + { + public MoveCloserToUsageViewModel(MoveCloserToUsageModel model) : base(model) + { + SetNewDeclarationStatementCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), (o) => SetNewDeclarationStatementExecute(o)); + } + + public Declaration Target => Model.Target; + + public string Instructions + { + get + { + if (Target == null) + { + return RefactoringsUI.MoveCloserToUsageDialog_InstructionsLabelText; + } + + return string.Format(RefactoringsUI.MoveCloserToUsageDialog_InstructionsLabelText, Target.IdentifierName); + } + } + + public DelegateCommand SetNewDeclarationStatementCommand { get; } + + void SetNewDeclarationStatementExecute(object param) + { + if (param is string newDeclarationStatement) + { + Model.DeclarationStatement = newDeclarationStatement; + } + + } + + } +} diff --git a/Rubberduck.Core/UI/Settings/IndenterSettingsViewModel.cs b/Rubberduck.Core/UI/Settings/IndenterSettingsViewModel.cs index ac4467a30b..c189a8dfdf 100644 --- a/Rubberduck.Core/UI/Settings/IndenterSettingsViewModel.cs +++ b/Rubberduck.Core/UI/Settings/IndenterSettingsViewModel.cs @@ -38,6 +38,7 @@ public IndenterSettingsViewModel(Configuration config, IConfigurationService ExportSettings(GetCurrentSettings())); diff --git a/Rubberduck.Core/UI/Settings/InspectionSettings.xaml b/Rubberduck.Core/UI/Settings/InspectionSettings.xaml index 54e0f40337..e4147eed24 100644 --- a/Rubberduck.Core/UI/Settings/InspectionSettings.xaml +++ b/Rubberduck.Core/UI/Settings/InspectionSettings.xaml @@ -65,7 +65,7 @@ Content="{Resx ResxName=Rubberduck.CodeAnalysis.CodeAnalysisUI, Key=CodeInspectionSettingsPage_FilterByDescription}" /> + Text="{Binding InspectionSettingsDescriptionFilter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Delay=400}" Height="26" />