From 7878277281cab2aa467b82429e801be11f078d1b Mon Sep 17 00:00:00 2001 From: Mathieu Guindon Date: Wed, 21 Apr 2021 01:53:18 -0400 Subject: [PATCH 1/9] fixes positional parameter finder logic, resolve selected parameter from argument in SelectionChangeService. --- Rubberduck.Core/UI/SelectionChangeService.cs | 1 + .../DeclarationCaching/DeclarationFinder.cs | 7 +++---- .../VBA/SelectedDeclarationProvider.cs | 21 +++++++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Rubberduck.Core/UI/SelectionChangeService.cs b/Rubberduck.Core/UI/SelectionChangeService.cs index 3e15f475ef..cb25628731 100644 --- a/Rubberduck.Core/UI/SelectionChangeService.cs +++ b/Rubberduck.Core/UI/SelectionChangeService.cs @@ -40,6 +40,7 @@ private void OnVbeSelectionChanged(object sender, EventArgs e) { Task.Run(() => { + var selectedDeclaration = _selectedDeclarationProvider.SelectedDeclaration(); var eventArgs = new DeclarationChangedEventArgs(_vbe, selectedDeclaration); DispatchSelectedDeclaration(eventArgs); diff --git a/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs b/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs index bb1cc114fd..ba4c637069 100644 --- a/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs +++ b/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs @@ -640,14 +640,13 @@ public ParameterDeclaration FindParameterOfNonDefaultMemberFromSimpleArgumentNot { // argument is positional: work out its index var argumentList = argumentExpression.GetAncestor(); - var arguments = argumentList.GetDescendents().ToArray(); + var arguments = argumentList.children.Where(t => (t is VBAParser.ArgumentContext)).ToArray(); var parameterIndex = arguments - .Select((arg, index) => arg.GetDescendent() == argumentExpression ? (arg, index) : (null, -1)) - .SingleOrDefault(item => item.arg != null).index; + .Select((arg, index) => (arg: arg as ParserRuleContext, index)) + .SingleOrDefault(item => item.arg.ContainsTokenIndex(argumentExpression.Start.TokenIndex)).index; parameter = parameters - .OrderBy(p => p.Selection) .Select((param, index) => (param, index)) .SingleOrDefault(item => item.index == parameterIndex).param; } diff --git a/Rubberduck.Parsing/VBA/SelectedDeclarationProvider.cs b/Rubberduck.Parsing/VBA/SelectedDeclarationProvider.cs index d459e1ee05..162580cbf9 100644 --- a/Rubberduck.Parsing/VBA/SelectedDeclarationProvider.cs +++ b/Rubberduck.Parsing/VBA/SelectedDeclarationProvider.cs @@ -84,6 +84,12 @@ public Declaration SelectedDeclaration(QualifiedSelection qualifiedSelection) return candidateViaConstantDeclaration; } + var candidateViaArgumentCallSite = SelectedDeclarationViaArgument(qualifiedSelection, finder); + if (candidateViaArgumentCallSite != null) + { + return candidateViaArgumentCallSite; + } + var candidateViaContainingMember = SelectedMember(qualifiedSelection); if (candidateViaContainingMember != null) { @@ -93,6 +99,21 @@ public Declaration SelectedDeclaration(QualifiedSelection qualifiedSelection) return SelectedModule(qualifiedSelection); } + private static ParameterDeclaration SelectedDeclarationViaArgument(QualifiedSelection qualifiedSelection, DeclarationFinder finder) + { + var members = finder.Members(qualifiedSelection.QualifiedName).Where(m => m.DeclarationType.HasFlag(DeclarationType.Procedure)); + var enclosingProcedure = members.SingleOrDefault(m => m.Context.GetSelection().Contains(qualifiedSelection.Selection)); + var context = enclosingProcedure?.Context.GetDescendents() + .FirstOrDefault(m => m.GetSelection().ContainsFirstCharacter(qualifiedSelection.Selection)); + + if (context != null) + { + return finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(context, enclosingProcedure); + } + + return null; + } + private static Declaration SelectedDeclarationViaReference(QualifiedSelection qualifiedSelection, DeclarationFinder finder) { var referencesInModule = finder.IdentifierReferences(qualifiedSelection.QualifiedName); From 743c3e63a2148086377244dfb27873d6287af292 Mon Sep 17 00:00:00 2001 From: Mathieu Guindon Date: Wed, 21 Apr 2021 10:13:54 -0400 Subject: [PATCH 2/9] fallback to the invoked procedure when inside an argument list --- .../VBA/DeclarationCaching/DeclarationFinder.cs | 5 +++++ .../VBA/SelectedDeclarationProvider.cs | 14 +++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs b/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs index ba4c637069..437d4e5daa 100644 --- a/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs +++ b/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs @@ -654,6 +654,11 @@ public ParameterDeclaration FindParameterOfNonDefaultMemberFromSimpleArgumentNot return parameter; } + public ModuleBodyElementDeclaration FindInvokedMemberFromArgumentExpressionContext(VBAParser.ArgumentExpressionContext argumentExpression, QualifiedModuleName module) + { + return CallingNonDefaultMember(argumentExpression, module); + } + private ModuleBodyElementDeclaration CallingNonDefaultMember(VBAParser.ArgumentExpressionContext argumentExpression, QualifiedModuleName module) { //todo: Make this work for default member calls. diff --git a/Rubberduck.Parsing/VBA/SelectedDeclarationProvider.cs b/Rubberduck.Parsing/VBA/SelectedDeclarationProvider.cs index 162580cbf9..c79a55b3fd 100644 --- a/Rubberduck.Parsing/VBA/SelectedDeclarationProvider.cs +++ b/Rubberduck.Parsing/VBA/SelectedDeclarationProvider.cs @@ -90,19 +90,26 @@ public Declaration SelectedDeclaration(QualifiedSelection qualifiedSelection) return candidateViaArgumentCallSite; } + // fallback to the containing member declaration if we're inside a procedure scope var candidateViaContainingMember = SelectedMember(qualifiedSelection); if (candidateViaContainingMember != null) { return candidateViaContainingMember; } + // otherwise fallback to the containing module declaration return SelectedModule(qualifiedSelection); } - private static ParameterDeclaration SelectedDeclarationViaArgument(QualifiedSelection qualifiedSelection, DeclarationFinder finder) + private static Declaration SelectedDeclarationViaArgument(QualifiedSelection qualifiedSelection, DeclarationFinder finder) { - var members = finder.Members(qualifiedSelection.QualifiedName).Where(m => m.DeclarationType.HasFlag(DeclarationType.Procedure)); + var members = finder.Members(qualifiedSelection.QualifiedName) + .Where(m => (m.DeclarationType.HasFlag(DeclarationType.Procedure) // includes PropertyLet and PropertySet and LibraryProcedure + || m.DeclarationType.HasFlag(DeclarationType.Function)) // includes PropertyGet and LibraryFunction + && !m.DeclarationType.HasFlag(DeclarationType.LibraryFunction) + && !m.DeclarationType.HasFlag(DeclarationType.LibraryProcedure)); var enclosingProcedure = members.SingleOrDefault(m => m.Context.GetSelection().Contains(qualifiedSelection.Selection)); + var context = enclosingProcedure?.Context.GetDescendents() .FirstOrDefault(m => m.GetSelection().ContainsFirstCharacter(qualifiedSelection.Selection)); @@ -111,7 +118,8 @@ private static ParameterDeclaration SelectedDeclarationViaArgument(QualifiedSele return finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(context, enclosingProcedure); } - return null; + // fallback to the invoked procedure declaration + return finder.FindInvokedMemberFromArgumentExpressionContext(context, qualifiedSelection.QualifiedName); } private static Declaration SelectedDeclarationViaReference(QualifiedSelection qualifiedSelection, DeclarationFinder finder) From ef55b69e4a6b7795b90f83e41cdc87b0d64f5620 Mon Sep 17 00:00:00 2001 From: Mathieu Guindon Date: Wed, 21 Apr 2021 15:14:15 -0400 Subject: [PATCH 3/9] include whitespace in argument selection --- .../NonReturningFunctionInspection.cs | 3 +- .../Concrete/ParameterCanBeByValInspection.cs | 3 +- ...ocedureCanBeWrittenAsFunctionInspection.cs | 3 +- .../UnassignedVariableUsageInspection.cs | 3 +- .../Concrete/VariableNotAssignedInspection.cs | 3 +- .../DeclarationCaching/DeclarationFinder.cs | 54 +++++++++++-------- .../VBA/SelectedDeclarationProvider.cs | 40 +++++++++++--- Rubberduck.VBEEditor/Selection.cs | 5 ++ 8 files changed, 81 insertions(+), 33 deletions(-) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/NonReturningFunctionInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/NonReturningFunctionInspection.cs index 919794f4c8..22e82c57c4 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/NonReturningFunctionInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/NonReturningFunctionInspection.cs @@ -71,7 +71,8 @@ private bool IsAssignedByRefArgument(Declaration enclosingProcedure, IdentifierR return false; } - var parameter = finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argExpression, enclosingProcedure); + var argument = argExpression.GetAncestor(); + var parameter = finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argument, enclosingProcedure); // note: not recursive, by design. return parameter != null diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ParameterCanBeByValInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ParameterCanBeByValInspection.cs index 2af67e9394..cb2d0b4929 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ParameterCanBeByValInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ParameterCanBeByValInspection.cs @@ -142,7 +142,8 @@ private static bool IsPotentiallyAssignedByRefArgument(QualifiedModuleName modul return false; } - var parameter = finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argExpression, module); + var argument = argExpression.GetAncestor(); + var parameter = finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argument, module); if (parameter == null) { diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureCanBeWrittenAsFunctionInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureCanBeWrittenAsFunctionInspection.cs index dbd001bc1c..fcb4f63c7b 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureCanBeWrittenAsFunctionInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ProcedureCanBeWrittenAsFunctionInspection.cs @@ -88,7 +88,8 @@ private static bool IsUsageAsAssignedToByRefArgument(IdentifierReference referen return false; } - var parameter = finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argExpression, reference.QualifiedModuleName); + var argument = argExpression.GetAncestor(); + var parameter = finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argument, reference.QualifiedModuleName); if (parameter == null) { diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/UnassignedVariableUsageInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/UnassignedVariableUsageInspection.cs index a8e03a9ae3..b3280565a6 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/UnassignedVariableUsageInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/UnassignedVariableUsageInspection.cs @@ -155,7 +155,8 @@ private static bool IsAssignedByRefArgument(Declaration enclosingProcedure, Iden return false; } - var parameter = finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argExpression, enclosingProcedure); + var argument = argExpression.GetAncestor(); + var parameter = finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argument, enclosingProcedure); // note: not recursive, by design. return parameter != null diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/VariableNotAssignedInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/VariableNotAssignedInspection.cs index fe5abbb010..d964a9e55e 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/VariableNotAssignedInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/VariableNotAssignedInspection.cs @@ -69,7 +69,8 @@ private static bool IsAssignedByRefArgument(Declaration enclosingProcedure, Iden return false; } - var parameter = finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argExpression, enclosingProcedure); + var argument = argExpression.GetAncestor(); + var parameter = finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argument, enclosingProcedure); // note: not recursive, by design. return parameter != null diff --git a/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs b/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs index 437d4e5daa..fb50fdf8cb 100644 --- a/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs +++ b/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs @@ -602,24 +602,25 @@ public IEnumerable MatchName(string name) : Enumerable.Empty(); } - public ParameterDeclaration FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(VBAParser.ArgumentExpressionContext argumentExpression, Declaration enclosingProcedure) + public ParameterDeclaration FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(VBAParser.ArgumentContext argument, Declaration enclosingProcedure) { - return FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argumentExpression, enclosingProcedure.QualifiedModuleName); + return FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argument, enclosingProcedure.QualifiedModuleName); } - public ParameterDeclaration FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(VBAParser.ArgumentExpressionContext argumentExpression, QualifiedModuleName module) + public ParameterDeclaration FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(VBAParser.ArgumentContext argument, QualifiedModuleName module) { //todo: Rename after making it work for more general cases. - - if (argumentExpression == null - || argumentExpression.GetDescendent() != null - || argumentExpression.BYVAL() != null) + var missingArgument = argument.missingArgument(); + var argumentExpression = argument.GetDescendent(); + if ((missingArgument == null && argumentExpression == null) + || argumentExpression?.GetDescendent() != null + || argumentExpression?.BYVAL() != null) { // not a simple argument, or argument is parenthesized and thus passed ByVal return null; } - var callingNonDefaultMember = CallingNonDefaultMember(argumentExpression, module); + var callingNonDefaultMember = CallingNonDefaultMember((ParserRuleContext)argumentExpression ?? missingArgument, module); if (callingNonDefaultMember == null) { // Either we could not resolve the call or there is a default member call involved. @@ -627,6 +628,7 @@ public ParameterDeclaration FindParameterOfNonDefaultMemberFromSimpleArgumentNot } var parameters = Parameters(callingNonDefaultMember); + var hasNamedArgs = argumentExpression?.GetAncestor()?.TryGetChildContext(out _) ?? false; ParameterDeclaration parameter; var namedArg = argumentExpression.GetAncestor(); @@ -639,35 +641,44 @@ public ParameterDeclaration FindParameterOfNonDefaultMemberFromSimpleArgumentNot else { // argument is positional: work out its index - var argumentList = argumentExpression.GetAncestor(); - var arguments = argumentList.children.Where(t => (t is VBAParser.ArgumentContext)).ToArray(); - - var parameterIndex = arguments - .Select((arg, index) => (arg: arg as ParserRuleContext, index)) - .SingleOrDefault(item => item.arg.ContainsTokenIndex(argumentExpression.Start.TokenIndex)).index; - + var argumentList = ((ParserRuleContext)argumentExpression ?? missingArgument).GetAncestor(); + var arguments = argumentList.children.Where(t => t is VBAParser.ArgumentContext).ToArray(); + var selection = argumentExpression?.GetSelection() ?? missingArgument.GetSelection(); + + var indexedArgs = arguments.Select((arg, index) => (arg: arg as ParserRuleContext, index)) + .Select(e => (arg: e.arg, e.index, selection:e.arg.GetSelection())) + .ToList(); + var indexedArg = indexedArgs.SingleOrDefault(item => item.selection.Contains(selection)); + if (indexedArg.arg == null) + { + return null; + } parameter = parameters .Select((param, index) => (param, index)) - .SingleOrDefault(item => item.index == parameterIndex).param; + .Single(item => item.index == indexedArg.index).param; } return parameter; } - public ModuleBodyElementDeclaration FindInvokedMemberFromArgumentExpressionContext(VBAParser.ArgumentExpressionContext argumentExpression, QualifiedModuleName module) + public ModuleBodyElementDeclaration FindInvokedMemberFromArgumentContext(VBAParser.ArgumentContext argument, QualifiedModuleName module) { - return CallingNonDefaultMember(argumentExpression, module); + var expression = (ParserRuleContext)argument.GetDescendent() + ?? argument.GetDescendent(); + return expression != null + ? CallingNonDefaultMember(expression, module) + : null; } - private ModuleBodyElementDeclaration CallingNonDefaultMember(VBAParser.ArgumentExpressionContext argumentExpression, QualifiedModuleName module) + private ModuleBodyElementDeclaration CallingNonDefaultMember(ParserRuleContext argumentExpressionOrMissingArgument, QualifiedModuleName module) { //todo: Make this work for default member calls. - var argumentList = argumentExpression.GetAncestor(); + var argumentList = argumentExpressionOrMissingArgument.GetAncestor(); var cannotHaveDefaultMemberCall = false; ParserRuleContext callingExpression; - switch (argumentList.Parent) + switch (argumentList?.Parent) { case VBAParser.CallStmtContext callStmt: cannotHaveDefaultMemberCall = true; @@ -680,7 +691,6 @@ private ModuleBodyElementDeclaration CallingNonDefaultMember(VBAParser.ArgumentE callingExpression = indexExpr.lExpression(); break; default: - //This should never happen. return null; } diff --git a/Rubberduck.Parsing/VBA/SelectedDeclarationProvider.cs b/Rubberduck.Parsing/VBA/SelectedDeclarationProvider.cs index c79a55b3fd..45d8df396c 100644 --- a/Rubberduck.Parsing/VBA/SelectedDeclarationProvider.cs +++ b/Rubberduck.Parsing/VBA/SelectedDeclarationProvider.cs @@ -107,19 +107,47 @@ private static Declaration SelectedDeclarationViaArgument(QualifiedSelection qua .Where(m => (m.DeclarationType.HasFlag(DeclarationType.Procedure) // includes PropertyLet and PropertySet and LibraryProcedure || m.DeclarationType.HasFlag(DeclarationType.Function)) // includes PropertyGet and LibraryFunction && !m.DeclarationType.HasFlag(DeclarationType.LibraryFunction) - && !m.DeclarationType.HasFlag(DeclarationType.LibraryProcedure)); + && !m.DeclarationType.HasFlag(DeclarationType.LibraryProcedure)); var enclosingProcedure = members.SingleOrDefault(m => m.Context.GetSelection().Contains(qualifiedSelection.Selection)); + if (enclosingProcedure == null) + { + return null; + } - var context = enclosingProcedure?.Context.GetDescendents() - .FirstOrDefault(m => m.GetSelection().ContainsFirstCharacter(qualifiedSelection.Selection)); + var allArguments = enclosingProcedure.Context.GetDescendents(); + var context = allArguments + .Where(arg => arg.missingArgument() == null) + .FirstOrDefault(m => + { + var isOnWhitespace = false; + if (m.TryGetPrecedingContext(out var whitespace)) + { + isOnWhitespace = whitespace.GetSelection().ContainsFirstCharacter(qualifiedSelection.Selection); + } + return isOnWhitespace || m.GetSelection().ContainsFirstCharacter(qualifiedSelection.Selection); + }); + + var skippedArg = allArguments + .Where(arg => arg.missingArgument() != null) + .FirstOrDefault(m => + { + var isOnWhitespace = false; + if (m.TryGetPrecedingContext(out var whitespace)) + { + isOnWhitespace = whitespace.GetSelection().ContainsFirstCharacter(qualifiedSelection.Selection); + } + return isOnWhitespace || m.GetSelection().ContainsFirstCharacter(qualifiedSelection.Selection); + }); + + context = context ?? skippedArg; if (context != null) { - return finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(context, enclosingProcedure); + return (Declaration)finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(context, enclosingProcedure) + ?? finder.FindInvokedMemberFromArgumentContext(context, qualifiedSelection.QualifiedName); // fallback to the invoked procedure declaration } - // fallback to the invoked procedure declaration - return finder.FindInvokedMemberFromArgumentExpressionContext(context, qualifiedSelection.QualifiedName); + return null; } private static Declaration SelectedDeclarationViaReference(QualifiedSelection qualifiedSelection, DeclarationFinder finder) diff --git a/Rubberduck.VBEEditor/Selection.cs b/Rubberduck.VBEEditor/Selection.cs index 2823a8bf3e..6bbd33a2b9 100644 --- a/Rubberduck.VBEEditor/Selection.cs +++ b/Rubberduck.VBEEditor/Selection.cs @@ -46,6 +46,11 @@ public bool ContainsFirstCharacter(Selection selection) return Contains(new Selection(selection.StartLine, selection.StartColumn, selection.StartLine, selection.StartColumn)); } + public Selection ExtendLeft(int positions = 1) + { + return new Selection(StartLine, Math.Max(StartColumn - positions, 1), EndLine, EndColumn); + } + public bool Contains(Selection selection) { // single line comparison From af5618628d865fd9daf78a11453f404d01ce8339 Mon Sep 17 00:00:00 2001 From: Mathieu Guindon Date: Wed, 21 Apr 2021 15:34:35 -0400 Subject: [PATCH 4/9] always use 'Object' instead of 'IDispatch' for the typename --- .../UI/Command/MenuItems/CommandBars/IContextFormatter.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs index bceb21df30..3ea1247dd1 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs @@ -125,10 +125,14 @@ private static string TypeName(Declaration declaration, bool multipleControls, s return RubberduckUI.ContextMultipleControlsSelection; } - var typeName = declaration.IsArray - ? $"{declaration.AsTypeName}()" + var friendlyTypeName = declaration.AsTypeName.Equals("IDispatch", System.StringComparison.InvariantCultureIgnoreCase) + ? "Object" : declaration.AsTypeName; + var typeName = declaration.IsArray + ? $"{friendlyTypeName}()" + : friendlyTypeName; + switch (declaration) { case ValuedDeclaration valued: From 0ca3f6f39374d09dfe1933132998322dae39c7e7 Mon Sep 17 00:00:00 2001 From: Mathieu Guindon Date: Wed, 21 Apr 2021 15:42:29 -0400 Subject: [PATCH 5/9] fixed broken test, addressed compiler warning --- RubberduckTests/CodeExplorer/MockedCodeExplorer.cs | 1 - RubberduckTests/Symbols/DeclarationFinderTests.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs b/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs index 19239633f9..afa469d9ab 100644 --- a/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs +++ b/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs @@ -34,7 +34,6 @@ using Rubberduck.VBEditor.SourceCodeHandling; using Rubberduck.VBEditor.Utility; using RubberduckTests.Settings; -using Rubberduck.Refactorings; using System.IO.Abstractions; namespace RubberduckTests.CodeExplorer diff --git a/RubberduckTests/Symbols/DeclarationFinderTests.cs b/RubberduckTests/Symbols/DeclarationFinderTests.cs index 70a8087a7b..5a20370b04 100644 --- a/RubberduckTests/Symbols/DeclarationFinderTests.cs +++ b/RubberduckTests/Symbols/DeclarationFinderTests.cs @@ -804,7 +804,7 @@ End Sub var expected = declarations.FirstOrDefault(decl => decl.IdentifierName.Equals("expected")); var enclosing = declarations.FirstOrDefault(decl => decl.IdentifierName.Equals("Bar")); - var context = enclosing?.Context.GetDescendent(); + var context = enclosing?.Context.GetDescendent(); var actual = state.DeclarationFinder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(context, enclosing); Assert.AreEqual(expected, actual); From 46452a1a8f1fa2b109117cf8293d7c750fd2c731 Mon Sep 17 00:00:00 2001 From: Mathieu Guindon Date: Wed, 21 Apr 2021 22:13:40 -0400 Subject: [PATCH 6/9] Make 'Find all References' argument-aware too --- .../CodeExplorerFindAllReferencesCommand.cs | 4 +-- .../ComCommands/FindAllReferencesCommand.cs | 8 ++--- .../CommandBars/IContextFormatter.cs | 4 +-- .../CommandBars/RubberduckCommandBar.cs | 5 ++- ...sService.cs => FindAllReferencesAction.cs} | 36 +++++++++++++------ .../DeclarationCaching/DeclarationFinder.cs | 2 -- .../Commands/FindAllReferencesTests.cs | 8 ++--- 7 files changed, 42 insertions(+), 25 deletions(-) rename Rubberduck.Core/UI/Controls/{FindAllReferencesService.cs => FindAllReferencesAction.cs} (86%) diff --git a/Rubberduck.Core/UI/CodeExplorer/Commands/CodeExplorerFindAllReferencesCommand.cs b/Rubberduck.Core/UI/CodeExplorer/Commands/CodeExplorerFindAllReferencesCommand.cs index a0080a6ebb..4ffdf4f883 100644 --- a/Rubberduck.Core/UI/CodeExplorer/Commands/CodeExplorerFindAllReferencesCommand.cs +++ b/Rubberduck.Core/UI/CodeExplorer/Commands/CodeExplorerFindAllReferencesCommand.cs @@ -19,11 +19,11 @@ public class CodeExplorerFindAllReferencesCommand : CodeExplorerCommandBase }; private readonly RubberduckParserState _state; - private readonly FindAllReferencesService _finder; + private readonly FindAllReferencesAction _finder; public CodeExplorerFindAllReferencesCommand( RubberduckParserState state, - FindAllReferencesService finder, + FindAllReferencesAction finder, IVbeEvents vbeEvents) : base(vbeEvents) { diff --git a/Rubberduck.Core/UI/Command/ComCommands/FindAllReferencesCommand.cs b/Rubberduck.Core/UI/Command/ComCommands/FindAllReferencesCommand.cs index 05e63d3ebc..a76ca7e010 100644 --- a/Rubberduck.Core/UI/Command/ComCommands/FindAllReferencesCommand.cs +++ b/Rubberduck.Core/UI/Command/ComCommands/FindAllReferencesCommand.cs @@ -18,7 +18,7 @@ public class FindAllReferencesCommand : ComCommandBase private readonly IDeclarationFinderProvider _declarationFinderProvider; private readonly ISelectedDeclarationProvider _selectedDeclarationProvider; private readonly IVBE _vbe; - private readonly FindAllReferencesService _finder; + private readonly FindAllReferencesAction _service; public FindAllReferencesCommand( IParserStatusProvider parserStatusProvider, @@ -26,7 +26,7 @@ public class FindAllReferencesCommand : ComCommandBase ISelectedDeclarationProvider selectedDeclarationProvider, IVBE vbe, ISearchResultsWindowViewModel viewModel, - FindAllReferencesService finder, + FindAllReferencesAction finder, IVbeEvents vbeEvents) : base(vbeEvents) { @@ -34,7 +34,7 @@ public class FindAllReferencesCommand : ComCommandBase _declarationFinderProvider = declarationFinderProvider; _selectedDeclarationProvider = selectedDeclarationProvider; _vbe = vbe; - _finder = finder; + _service = finder; AddToCanExecuteEvaluation(SpecialEvaluateCanExecute); } @@ -77,7 +77,7 @@ protected override void OnExecute(object parameter) return; } - _finder.FindAllReferences(declaration); + _service.FindAllReferences(declaration); } private Declaration FindTarget(object parameter) diff --git a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs index 3ea1247dd1..337772c7ab 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs @@ -125,9 +125,9 @@ private static string TypeName(Declaration declaration, bool multipleControls, s return RubberduckUI.ContextMultipleControlsSelection; } - var friendlyTypeName = declaration.AsTypeName.Equals("IDispatch", System.StringComparison.InvariantCultureIgnoreCase) + var friendlyTypeName = "IDispatch".Equals(declaration.AsTypeName, System.StringComparison.InvariantCultureIgnoreCase) ? "Object" - : declaration.AsTypeName; + : declaration.AsTypeName ?? string.Empty; var typeName = declaration.IsArray ? $"{friendlyTypeName}()" diff --git a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs index af1e1bf217..17a32ddad9 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs @@ -64,7 +64,10 @@ private void OnSelectionChange(object sender, DeclarationChangedEventArgs e) caption = e.FallbackCaption; } - var refCount = e.Declaration?.References.Count() ?? 0; + var refCount = (e.Declaration?.References.Count() ?? 0) + + ((e.Declaration is ParameterDeclaration parameter) + ? parameter.ArgumentReferences.Count() + : 0); var description = e.Declaration?.DescriptionString ?? string.Empty; //& renders the next character as if it was an accelerator. SetContextSelectionCaption(caption?.Replace("&", "&&"), refCount, description); diff --git a/Rubberduck.Core/UI/Controls/FindAllReferencesService.cs b/Rubberduck.Core/UI/Controls/FindAllReferencesAction.cs similarity index 86% rename from Rubberduck.Core/UI/Controls/FindAllReferencesService.cs rename to Rubberduck.Core/UI/Controls/FindAllReferencesAction.cs index a2e25530e0..6e9259d0e9 100644 --- a/Rubberduck.Core/UI/Controls/FindAllReferencesService.cs +++ b/Rubberduck.Core/UI/Controls/FindAllReferencesAction.cs @@ -12,7 +12,7 @@ namespace Rubberduck.UI.Controls { - public class FindAllReferencesService : IDisposable + public class FindAllReferencesAction : IDisposable { private readonly Logger _logger = LogManager.GetCurrentClassLogger(); private readonly INavigateCommand _navigateCommand; @@ -22,7 +22,7 @@ public class FindAllReferencesService : IDisposable private readonly SearchResultPresenterInstanceManager _presenterService; private readonly IUiDispatcher _uiDispatcher; - public FindAllReferencesService( + public FindAllReferencesAction( INavigateCommand navigateCommand, IMessageBox messageBox, RubberduckParserState state, @@ -92,7 +92,21 @@ public void FindAllReferences(Declaration declaration, ReferenceInfo reference) return; } - var usages = _state.DeclarationFinder.FindAllReferenceUsesInProject(project, reference, out var referenceProject); + var usages = _state.DeclarationFinder.FindAllReferenceUsesInProject(project, reference, out var referenceProject) + .Select(usage => + new SearchResultItem(usage.ParentNonScoping, + new NavigateCodeEventArgs(usage.QualifiedModuleName, usage.Selection), + GetModuleLine(usage.QualifiedModuleName, usage.Selection.StartLine))) + .ToList(); + + if (declaration is ParameterDeclaration parameter) + { + usages.AddRange(parameter.ArgumentReferences.Select(usage => + new SearchResultItem(usage.ParentNonScoping, + new NavigateCodeEventArgs(usage.QualifiedModuleName, usage.Selection), + GetModuleLine(usage.QualifiedModuleName, usage.Selection.StartLine)))); + } + if (!usages.Any()) { _messageBox.NotifyWarn(string.Format(RubberduckUI.AllReferences_NoneFoundReference, referenceProject.IdentifierName), RubberduckUI.Rubberduck); @@ -130,13 +144,8 @@ private string GetModuleLine(QualifiedModuleName module, int line) } } - private SearchResultsViewModel CreateViewModel(ProjectDeclaration project, ProjectDeclaration reference, IEnumerable usages) + private SearchResultsViewModel CreateViewModel(ProjectDeclaration project, ProjectDeclaration reference, IEnumerable results) { - var results = usages.Select(usage => - new SearchResultItem(usage.ParentNonScoping, - new NavigateCodeEventArgs(usage.QualifiedModuleName, usage.Selection), - GetModuleLine(usage.QualifiedModuleName, usage.Selection.StartLine))); - var viewModel = new SearchResultsViewModel(_navigateCommand, string.Format(RubberduckUI.SearchResults_AllReferencesTabFormat, reference.IdentifierName), project, results); @@ -152,7 +161,14 @@ private SearchResultsViewModel CreateViewModel(Declaration declaration) new SearchResultItem( reference.ParentNonScoping, new NavigateCodeEventArgs(reference.QualifiedModuleName, reference.Selection), - GetModuleLine(reference.QualifiedModuleName, reference.Selection.StartLine))); + GetModuleLine(reference.QualifiedModuleName, reference.Selection.StartLine))) + .Concat((declaration is ParameterDeclaration parameter) + ? parameter.ArgumentReferences.Select(argument => + new SearchResultItem( + argument.ParentNonScoping, + new NavigateCodeEventArgs(argument.QualifiedModuleName, argument.Selection), + GetModuleLine(argument.QualifiedModuleName, argument.Selection.StartLine))) + : Enumerable.Empty()); var accessor = declaration.DeclarationType.HasFlag(DeclarationType.PropertyGet) ? "(get)" : declaration.DeclarationType.HasFlag(DeclarationType.PropertyLet) ? "(let)" diff --git a/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs b/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs index fb50fdf8cb..38261831c7 100644 --- a/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs +++ b/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs @@ -1591,12 +1591,10 @@ public bool IsReferenceUsedInProject(ProjectDeclaration project, ReferenceInfo r } referenceProject = GetProjectDeclarationForReference(reference); - if (!_referencesByProjectId.TryGetValue(referenceProject.ProjectId, out var directReferences)) { return output; } - output.AddRange(directReferences); var projectId = referenceProject.ProjectId; diff --git a/RubberduckTests/Commands/FindAllReferencesTests.cs b/RubberduckTests/Commands/FindAllReferencesTests.cs index f6b6c8a4ee..327a0c68f6 100644 --- a/RubberduckTests/Commands/FindAllReferencesTests.cs +++ b/RubberduckTests/Commands/FindAllReferencesTests.cs @@ -525,11 +525,11 @@ private static SearchResultsWindowViewModel ArrangeSearchResultsWindowViewModel( return new SearchResultsWindowViewModel(); } - private static FindAllReferencesService ArrangeFindAllReferencesService(RubberduckParserState state, + private static FindAllReferencesAction ArrangeFindAllReferencesService(RubberduckParserState state, ISearchResultsWindowViewModel viewModel, INavigateCommand navigateCommand = null, IMessageBox messageBox = null, SearchResultPresenterInstanceManager presenterService = null, IUiDispatcher uiDispatcher = null) { - return new FindAllReferencesService( + return new FindAllReferencesAction( navigateCommand ?? new Mock().Object, messageBox ?? new Mock().Object, state, @@ -547,13 +547,13 @@ private static SearchResultsWindowViewModel ArrangeSearchResultsWindowViewModel( } private static FindAllReferencesCommand ArrangeFindAllReferencesCommand(RubberduckParserState state, - Mock vbe, ISearchResultsWindowViewModel viewModel, FindAllReferencesService finder) + Mock vbe, ISearchResultsWindowViewModel viewModel, FindAllReferencesAction finder) { return ArrangeFindAllReferencesCommand(state, vbe, viewModel, finder, MockVbeEvents.CreateMockVbeEvents(vbe)); } private static FindAllReferencesCommand ArrangeFindAllReferencesCommand(RubberduckParserState state, - Mock vbe, ISearchResultsWindowViewModel viewModel, FindAllReferencesService finder, + Mock vbe, ISearchResultsWindowViewModel viewModel, FindAllReferencesAction finder, Mock vbeEvents) { var selectionService = new SelectionService(vbe.Object, state.ProjectsProvider); From 3b0d094283bdeeecad3ca66c4a7c3b270227cf91 Mon Sep 17 00:00:00 2001 From: Mathieu Guindon Date: Fri, 23 Apr 2021 02:56:14 -0400 Subject: [PATCH 7/9] Added ForbiddenAttribute to mark keywords, reserved identifier names, and other forbidden names; adjusted name validator accordingly. --- .../ExtractMethod/ExtractedParameter.cs | 1 + .../CommandBars/IContextFormatter.cs | 36 +- .../UI/FindSymbol/FindSymbolControl.xaml | 2 +- .../UI/FindSymbol/FindSymbolControl.xaml.cs | 2 +- .../UI/Refactorings/ExtractMethodDialog.cs | 6 +- Rubberduck.Refactorings/Common/CodeBuilder.cs | 1 + .../Common/VBAIdentifierValidator.cs | 10 +- .../ExtractInterfaceRefactoringAction.cs | 1 + ...terfaceImplementationsRefactoringAction.cs | 1 + Rubberduck.Resources/Tokens.cs | 384 ++++++++++++++++++ Rubberduck.sln.DotSettings | 2 + 11 files changed, 425 insertions(+), 21 deletions(-) create mode 100644 Rubberduck.Resources/Tokens.cs create mode 100644 Rubberduck.sln.DotSettings diff --git a/Rubberduck.Core/Refactorings/ExtractMethod/ExtractedParameter.cs b/Rubberduck.Core/Refactorings/ExtractMethod/ExtractedParameter.cs index d686f162bb..3c6d5ad147 100644 --- a/Rubberduck.Core/Refactorings/ExtractMethod/ExtractedParameter.cs +++ b/Rubberduck.Core/Refactorings/ExtractMethod/ExtractedParameter.cs @@ -1,5 +1,6 @@ using Rubberduck.Parsing.Grammar; using Rubberduck.Resources; +using Tokens = Rubberduck.Resources.Tokens; namespace Rubberduck.Refactorings.ExtractMethod { diff --git a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs index 337772c7ab..f5dce69bae 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs @@ -42,7 +42,13 @@ public string Format(ICodePane activeCodePane, Declaration declaration) public string Format(Declaration declaration, bool multipleControls) { - return declaration == null ? string.Empty : FormatDeclaration(declaration, multipleControls); + if (declaration == null) + { + return string.Empty; + } + + // designer, there is no code pane selection + return FormatDeclaration(declaration, multipleControls); } private string FormatDeclaration(Declaration declaration, bool multipleControls = false) @@ -72,18 +78,18 @@ private string FormatDeclaration(Declaration declaration, bool multipleControls if (declaration.ParentDeclaration.DeclarationType.HasFlag(DeclarationType.Module)) { // fields - var withEvents = declaration.IsWithEvents ? "(WithEvents) " : string.Empty; + var withEvents = declaration.IsWithEvents ? $"({Tokens.WithEvents}) " : string.Empty; return $"{withEvents}{moduleName}.{declaration.IdentifierName} {typeName}"; } } if (declaration.DeclarationType.HasFlag(DeclarationType.Member)) { - var formattedDeclaration = declaration.QualifiedName.ToString(); + var formattedDeclaration = $"{declaration.QualifiedName}"; if (declaration.DeclarationType == DeclarationType.Function || declaration.DeclarationType == DeclarationType.PropertyGet) { - formattedDeclaration += typeName; + formattedDeclaration += $" {typeName}"; } return formattedDeclaration; @@ -103,16 +109,16 @@ private string FormatDeclaration(Declaration declaration, bool multipleControls case DeclarationType.Enumeration: case DeclarationType.UserDefinedType: return !declaration.IsUserDefined - // built-in enums & UDT's don't have a module - ? $"{Path.GetFileName(moduleName.ProjectPath)};{moduleName.ProjectName}.{declaration.IdentifierName}" + // built-in enums & UDTs don't have a module + ? $"{Path.GetFileName(moduleName.ProjectPath)};{declaration.IdentifierName}" : moduleName.ToString(); case DeclarationType.EnumerationMember: case DeclarationType.UserDefinedTypeMember: return declaration.IsUserDefined ? $"{moduleName}.{declaration.ParentDeclaration.IdentifierName}.{declaration.IdentifierName} {typeName}" - : $"{Path.GetFileName(moduleName.ProjectPath)};{moduleName.ProjectName}.{declaration.ParentDeclaration.IdentifierName}.{declaration.IdentifierName} {typeName}"; + : $"{Path.GetFileName(moduleName.ProjectPath)};{declaration.ParentDeclaration.IdentifierName}.{declaration.IdentifierName} {typeName}"; case DeclarationType.ComAlias: - return $"{Path.GetFileName(moduleName.ProjectPath)};{moduleName.ProjectName}.{declaration.IdentifierName} (alias:{declaration.AsTypeName})"; + return $"{Path.GetFileName(moduleName.ProjectPath)};{declaration.IdentifierName} (alias:{declaration.AsTypeName})"; } return string.Empty; @@ -125,22 +131,20 @@ private static string TypeName(Declaration declaration, bool multipleControls, s return RubberduckUI.ContextMultipleControlsSelection; } - var friendlyTypeName = "IDispatch".Equals(declaration.AsTypeName, System.StringComparison.InvariantCultureIgnoreCase) - ? "Object" + var typeName = Tokens.IDispatch.Equals(declaration.AsTypeName, System.StringComparison.InvariantCultureIgnoreCase) + ? Tokens.Object : declaration.AsTypeName ?? string.Empty; - var typeName = declaration.IsArray - ? $"{friendlyTypeName}()" - : friendlyTypeName; + var friendlyTypeName = declaration.IsArray ? $"{typeName}()" : typeName; switch (declaration) { case ValuedDeclaration valued: - return $"({declarationType}{(string.IsNullOrEmpty(typeName) ? string.Empty : ":" + typeName)}{(string.IsNullOrEmpty(valued.Expression) ? string.Empty : $" = {valued.Expression}")})"; + return $"({declarationType}{(string.IsNullOrEmpty(friendlyTypeName) ? string.Empty : ":" + friendlyTypeName)}{(string.IsNullOrEmpty(valued.Expression) ? string.Empty : $" = {valued.Expression}")})"; case ParameterDeclaration parameter: - return $"({declarationType}{(string.IsNullOrEmpty(typeName) ? string.Empty : ":" + typeName)}{(string.IsNullOrEmpty(parameter.DefaultValue) ? string.Empty : $" = {parameter.DefaultValue}")})"; + return $"({declarationType}{(string.IsNullOrEmpty(friendlyTypeName) ? string.Empty : ":" + friendlyTypeName)}{(string.IsNullOrEmpty(parameter.DefaultValue) ? string.Empty : $" = {parameter.DefaultValue}")})"; default: - return $"({declarationType}{(string.IsNullOrEmpty(typeName) ? string.Empty : ":" + typeName)})"; + return $"({declarationType}{(string.IsNullOrEmpty(friendlyTypeName) ? string.Empty : ":" + friendlyTypeName)})"; } } } diff --git a/Rubberduck.Core/UI/FindSymbol/FindSymbolControl.xaml b/Rubberduck.Core/UI/FindSymbol/FindSymbolControl.xaml index b35340cbc3..7234df4bb5 100644 --- a/Rubberduck.Core/UI/FindSymbol/FindSymbolControl.xaml +++ b/Rubberduck.Core/UI/FindSymbol/FindSymbolControl.xaml @@ -25,7 +25,7 @@ - item.GetValue(null)).Cast().Select(item => item); + var tokenValues = typeof(Tokens).GetFields() + .Where(item => !item.GetCustomAttributes().Any()) + .Select(item => item.GetValue(null)).Cast().Select(item => item); OkButton.Enabled = MethodName != OldMethodName && char.IsLetter(MethodName.FirstOrDefault()) diff --git a/Rubberduck.Refactorings/Common/CodeBuilder.cs b/Rubberduck.Refactorings/Common/CodeBuilder.cs index 3f520f36d4..263bfba39c 100644 --- a/Rubberduck.Refactorings/Common/CodeBuilder.cs +++ b/Rubberduck.Refactorings/Common/CodeBuilder.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Tokens = Rubberduck.Resources.Tokens; namespace Rubberduck.Refactorings { diff --git a/Rubberduck.Refactorings/Common/VBAIdentifierValidator.cs b/Rubberduck.Refactorings/Common/VBAIdentifierValidator.cs index ab73c78335..bddc7141c7 100644 --- a/Rubberduck.Refactorings/Common/VBAIdentifierValidator.cs +++ b/Rubberduck.Refactorings/Common/VBAIdentifierValidator.cs @@ -5,13 +5,19 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Reflection; +using Tokens = Rubberduck.Resources.Tokens; namespace Rubberduck.Refactorings.Common { public static class VBAIdentifierValidator { - private static IEnumerable ReservedIdentifiers = - typeof(Tokens).GetFields().Select(item => item.GetValue(null).ToString()).ToArray(); + // NOTE: ForbiddenAttribute marks the tokens that don't compile as identifier names. Includes "bad but legal" names. + // TODO: Compare with the unfiltered tokens if a client needs to tell "bad but legal" from "bad and illegal" names. + private static readonly IEnumerable ReservedIdentifiers = + typeof(Tokens).GetFields() + .Where(item => item.GetType().GetCustomAttributes().Any()) + .Select(item => item.GetValue(null).ToString()).ToArray(); /// /// Predicate function determining if an identifier string's content will trigger a result for the UseMeaningfulNames inspection. diff --git a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoringAction.cs b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoringAction.cs index 789ac6ce48..7737a6336c 100644 --- a/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoringAction.cs +++ b/Rubberduck.Refactorings/ExtractInterface/ExtractInterfaceRefactoringAction.cs @@ -15,6 +15,7 @@ using Rubberduck.VBEditor.ComManagement; using Rubberduck.VBEditor.SafeComWrappers; using Rubberduck.VBEditor.Utility; +using Tokens = Rubberduck.Resources.Tokens; namespace Rubberduck.Refactorings.ExtractInterface { diff --git a/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs b/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs index 415dbb09c5..81c6fc4c50 100644 --- a/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs +++ b/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs @@ -5,6 +5,7 @@ using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; using Rubberduck.Resources; +using Tokens = Rubberduck.Resources.Tokens; namespace Rubberduck.Refactorings.AddInterfaceImplementations { diff --git a/Rubberduck.Resources/Tokens.cs b/Rubberduck.Resources/Tokens.cs new file mode 100644 index 0000000000..0e369f425a --- /dev/null +++ b/Rubberduck.Resources/Tokens.cs @@ -0,0 +1,384 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Rubberduck.Resources +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + public static class Tokens + { + [Forbidden] + public static readonly string Abs = "Abs"; + [Forbidden] + public static readonly string AddressOf = "AddressOf"; + [Forbidden] + public static readonly string And = "And"; + [Forbidden] + public static readonly string Any = "Any"; + [Forbidden] + public static readonly string As = "As"; + public static readonly string Asc = "Asc"; + [Forbidden] + public static readonly string Attribute = "Attribute"; + [Forbidden] + public static readonly string Array = "Array"; + public static readonly string Base = "Base"; + public static readonly string Beep = "Beep"; + public static readonly string Binary = "Binary"; + [Forbidden] + public static readonly string Boolean = "Boolean"; + [Forbidden] + public static readonly string ByRef = "ByRef"; + [Forbidden] + public static readonly string Byte = "Byte"; + [Forbidden] + public static readonly string ByVal = "ByVal"; + [Forbidden] + public static readonly string Call = "Call"; + [Forbidden] + public static readonly string Case = "Case"; + [Forbidden] + public static readonly string CBool = "CBool"; + [Forbidden] + public static readonly string CByte = "CByte"; + [Forbidden] + public static readonly string CCur = "CCur"; + [Forbidden] + public static readonly string CDate = "CDate"; + [Forbidden] + public static readonly string CDbl = "CDbl"; + [Forbidden] + public static readonly string CDec = "CDec"; + public static readonly string ChDir = "ChDir"; + public static readonly string ChDrive = "ChDrive"; + public static readonly string Chr = "Chr"; + public static readonly string ChrB = "ChrB"; + public static readonly string ChrW = "ChrW"; + [Forbidden] + public static readonly string CInt = "CInt"; + [Forbidden] + public static readonly string CLng = "CLng"; + public static readonly string CLngLng = "CLngLng"; + [Forbidden] + public static readonly string CLngPtr = "CLngPtr"; + public static readonly string Close = "Close"; + public static readonly string Command = "Command"; + [Forbidden] + public static readonly string CommentMarker = "'"; + public static readonly string Compare = "Compare"; + [Forbidden] + public static readonly string Const = "Const"; + public static readonly string Cos = "Cos"; + [Forbidden] + public static readonly string CSng = "CSng"; + [Forbidden] + public static readonly string CStr = "CStr"; + public static readonly string CurDir = "CurDir"; + [Forbidden] + public static readonly string Currency = "Currency"; + [Forbidden] + public static readonly string CVar = "CVar"; + [Forbidden] + public static readonly string CVErr = "CVErr"; + public static readonly string Data = "Data"; + [Forbidden] + public static readonly string Date = "Date"; + public static readonly string DateValue = "DateValue"; + public static readonly string Day = "Day"; + [Forbidden] + public static readonly string Debug = "Debug"; + [Forbidden] + public static readonly string Decimal = "Decimal"; + [Forbidden] + public static readonly string Declare = "Declare"; + [Forbidden] + public static readonly string DefBool = "DefBool"; + [Forbidden] + public static readonly string DefByte = "DefByte"; + [Forbidden] + public static readonly string DefCur = "DefCur"; + [Forbidden] + public static readonly string DefDate = "DefDate"; + [Forbidden] + public static readonly string DefDbl = "DefDbl"; + [Forbidden] + public static readonly string DefInt = "DefInt"; + [Forbidden] + public static readonly string DefLng = "DefLng"; + public static readonly string DefLngLng = "DefLngLng"; + [Forbidden] + public static readonly string DefLngPtr = "DefLngptr"; + [Forbidden] + public static readonly string DefObj = "DefObj"; + [Forbidden] + public static readonly string DefSng = "DefSng"; + [Forbidden] + public static readonly string DefStr = "DefStr"; + [Forbidden] + public static readonly string DefVar = "DefVar"; + [Forbidden] + public static readonly string Dim = "Dim"; + public static readonly string Dir = "Dir"; + [Forbidden] + public static readonly string Do = "Do"; + [Forbidden] + public static readonly string DoEvents = "DoEvents"; + [Forbidden] + public static readonly string Double = "Double"; + [Forbidden] + public static readonly string Each = "Each"; + [Forbidden] + public static readonly string Else = "Else"; + [Forbidden] + public static readonly string ElseIf = "ElseIf"; + [Forbidden] + public static readonly string Empty = "Empty"; + [Forbidden] + public static readonly string End = "End"; + [Forbidden] + public static readonly string Enum = "Enum"; + public static readonly string Environ = "Environ"; + public static readonly string EOF = "EOF"; + [Forbidden] + public static readonly string Eqv = "Eqv"; + [Forbidden] + public static readonly string Erase = "Erase"; + public static readonly string Err = "Err"; + public static readonly string Error = "Error"; + [Forbidden] + public static readonly string Event = "Event"; + [Forbidden] + public static readonly string Exit = "Exit"; + public static readonly string Exp = "Exp"; + public static readonly string Explicit = "Explicit"; + [Forbidden] + public static readonly string False = "False"; + [Forbidden] + public static readonly string Fix = "Fix"; + [Forbidden] + public static readonly string For = "For"; + public static readonly string Format = "Format"; + public static readonly string FreeFile = "FreeFile"; + [Forbidden] + public static readonly string Friend = "Friend"; + [Forbidden] + public static readonly string Function = "Function"; + [Forbidden] + public static readonly string Get = "Get"; + [Forbidden] + public static readonly string Global = "Global"; + [Forbidden] + public static readonly string GoSub = "GoSub"; + [Forbidden] + public static readonly string GoTo = "GoTo"; + public static readonly string Hex = "Hex"; + public static readonly string Hour = "Hour"; + public static readonly string IDispatch = "IDispatch"; + [Forbidden] + public static readonly string If = "If"; + [Forbidden] + public static readonly string Imp = "Imp"; + [Forbidden] + public static readonly string Implements = "Implements"; + [Forbidden] + public static readonly string In = "In"; + [Forbidden] + public static readonly string Input = "Input"; + [Forbidden] + public static readonly string InputB = "InputB"; + public static readonly string InputBox = "InputBox"; + public static readonly string InStr = "InStr"; + [Forbidden] + public static readonly string Int = "Int"; + [Forbidden] + public static readonly string Integer = "Integer"; + [Forbidden] + public static readonly string Is = "Is"; + public static readonly string IsDate = "IsDate"; + public static readonly string IsEmpty = "IsEmpty"; + public static readonly string IsNull = "IsNull"; + public static readonly string IsNumeric = "IsNumeric"; + public static readonly string Join = "Join"; + public static readonly string Kill = "Kill"; + [Forbidden] + public static readonly string LBound = "LBound"; + public static readonly string LCase = "LCase"; + public static readonly string Left = "Left"; + public static readonly string LeftB = "LeftB"; + [Forbidden] + public static readonly string Len = "Len"; + [Forbidden] + public static readonly string LenB = "LenB"; + [Forbidden] + public static readonly string Let = "Let"; + [Forbidden] + public static readonly string Like = "Like"; + public static readonly string Line = "Line"; + [Forbidden] + public static readonly string LineContinuation = " _"; + [Forbidden] + public static readonly string Lock = "Lock"; + public static readonly string LOF = "LOF"; + [Forbidden] + public static readonly string Long = "Long"; + public static readonly string LongLong = "LongLong"; + [Forbidden] + public static readonly string LongPtr = "LongPtr"; + [Forbidden] + public static readonly string Loop = "Loop"; + [Forbidden] + public static readonly string LSet = "LSet"; + public static readonly string LTrim = "LTrim"; + [Forbidden] + public static readonly string Me = "Me"; + public static readonly string Mid = "Mid"; + public static readonly string MidB = "MidB"; + public static readonly string Minute = "Minute"; + public static readonly string MkDir = "MkDir"; + [Forbidden] + public static readonly string Mod = "Mod"; + public static readonly string Month = "Month"; + public static readonly string MsgBox = "MsgBox"; + [Forbidden] + public static readonly string New = "New"; + [Forbidden] + public static readonly string Next = "Next"; + [Forbidden] + public static readonly string Not = "Not"; + [Forbidden] + public static readonly string Nothing = "Nothing"; + public static readonly string Now = "Now"; + [Forbidden] + public static readonly string Null = "Null"; + public static readonly string Object = "Object"; + public static readonly string Oct = "Oct"; + [Forbidden] + public static readonly string On = "On"; + [Forbidden] + public static readonly string Open = "Open"; + [Forbidden] + public static readonly string Option = "Option"; + [Forbidden] + public static readonly string Optional = "Optional"; + [Forbidden] + public static readonly string Or = "Or"; + public static readonly string Output = "Output"; + [Forbidden] + public static readonly string ParamArray = "ParamArray"; + [Forbidden] + public static readonly string Preserve = "Preserve"; + [Forbidden] + public static readonly string Print = "Print"; + [Forbidden] + public static readonly string Private = "Private"; + public static readonly string Property = "Property"; + [Forbidden] + public static readonly string Public = "Public"; + [Forbidden] + public static readonly string Put = "Put"; + [Forbidden] + public static readonly string RaiseEvent = "RaiseEvent"; + public static readonly string Random = "Random"; + public static readonly string Randomize = "Randomize"; + public static readonly string Read = "Read"; + [Forbidden] + public static readonly string ReDim = "ReDim"; + [Forbidden] + public static readonly string Rem = "Rem"; + [Forbidden] + public static readonly string Resume = "Resume"; + [Forbidden] + public static readonly string Return = "Return"; + [Forbidden] + public static readonly string RSet = "RSet"; + public static readonly string Right = "Right"; + public static readonly string RightB = "RightB"; + public static readonly string RmDir = "RmDir"; + public static readonly string Rnd = "Rnd"; + public static readonly string RTrim = "RTrim"; + public static readonly string Second = "Second"; + [Forbidden] + public static readonly string Seek = "Seek"; + [Forbidden] + public static readonly string Select = "Select"; + [Forbidden] + public static readonly string Set = "Set"; + [Forbidden] + public static readonly string Shared = "Shared"; + public static readonly string Shell = "Shell"; + public static readonly string Sin = "Sin"; + [Forbidden] + public static readonly string Single = "Single"; + public static readonly string Sng = "Sng"; + [Forbidden] + public static readonly string Space = "Space"; + public static readonly string Spc = "Spc"; + public static readonly string Split = "Split"; + public static readonly string Sqr = "Sqr"; + public static readonly string Static = "Static"; + public static readonly string Step = "Step"; + [Forbidden] + public static readonly string Stop = "Stop"; + public static readonly string Str = "Str"; + public static readonly string StrConv = "StrConv"; + [Forbidden] + public static readonly string String = "String"; + public static readonly string StrPtr = "StrPtr"; + [Forbidden] + public static readonly string Sub = "Sub"; + [Forbidden] + public static readonly string Then = "Then"; + public static readonly string Time = "Time"; + [Forbidden] + public static readonly string To = "To"; + public static readonly string Trim = "Trim"; + [Forbidden] + public static readonly string True = "True"; + [Forbidden] + public static readonly string Type = "Type"; + public static readonly string TypeName = "TypeName"; + [Forbidden] + public static readonly string TypeOf = "TypeOf"; + [Forbidden] + public static readonly string UBound = "UBound"; + public static readonly string UCase = "UCase"; + [Forbidden] + public static readonly string Unlock = "Unlock"; + [Forbidden] + public static readonly string Until = "Until"; + public static readonly string Val = "Val"; + [Forbidden] + public static readonly string Variant = "Variant"; + public static readonly string vbBack = "vbBack"; + public static readonly string vbCr = "vbCr"; + public static readonly string vbCrLf = "vbCrLf"; + public static readonly string vbFormFeed = "vbFormFeed"; + public static readonly string vbLf = "vbLf"; + public static readonly string vbNewLine = "vbNewLine"; + public static readonly string vbNullChar = "vbNullChar"; + public static readonly string vbNullString = "vbNullString"; + public static readonly string vbTab = "vbTab"; + public static readonly string vbVerticalTab = "vbVerticalTab"; + public static readonly string WeekDay = "WeekDay"; + [Forbidden] + public static readonly string Wend = "Wend"; + [Forbidden] + public static readonly string While = "While"; + public static readonly string Width = "Width"; + [Forbidden] + public static readonly string With = "With"; + [Forbidden] + public static readonly string WithEvents = "WithEvents"; + [Forbidden] + public static readonly string Write = "Write"; + [Forbidden] + public static readonly string XOr = "Xor"; + public static readonly string Year = "Year"; + } + + /// + /// Identifies a static string that isn't a legal identifier name for user code, e.g. keyword or reserved identifier. + /// + public class ForbiddenAttribute : Attribute + { + } +} diff --git a/Rubberduck.sln.DotSettings b/Rubberduck.sln.DotSettings new file mode 100644 index 0000000000..979f07c7eb --- /dev/null +++ b/Rubberduck.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file From 43a4d41d328b39598be68dc69487482c650d4330 Mon Sep 17 00:00:00 2001 From: Mathieu Guindon Date: Fri, 23 Apr 2021 03:10:01 -0400 Subject: [PATCH 8/9] cleaned up --- .../UI/Refactorings/ExtractMethodDialog.cs | 6 +----- .../Common/VBAIdentifierValidator.cs | 14 ++++--------- Rubberduck.Resources/Tokens.cs | 21 ++++++++++++------- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/Rubberduck.Core/UI/Refactorings/ExtractMethodDialog.cs b/Rubberduck.Core/UI/Refactorings/ExtractMethodDialog.cs index cd5e548bde..3921542d5d 100644 --- a/Rubberduck.Core/UI/Refactorings/ExtractMethodDialog.cs +++ b/Rubberduck.Core/UI/Refactorings/ExtractMethodDialog.cs @@ -195,13 +195,9 @@ public string MethodName private void ValidateName() { - var tokenValues = typeof(Tokens).GetFields() - .Where(item => !item.GetCustomAttributes().Any()) - .Select(item => item.GetValue(null)).Cast().Select(item => item); - OkButton.Enabled = MethodName != OldMethodName && char.IsLetter(MethodName.FirstOrDefault()) - && !tokenValues.Contains(MethodName, StringComparer.InvariantCultureIgnoreCase) + && !Tokens.IllegalIdentifierNames.Contains(MethodName, StringComparer.InvariantCultureIgnoreCase) && !MethodName.Any(c => !char.IsLetterOrDigit(c) && c != '_'); InvalidNameValidationIcon.Visible = !OkButton.Enabled; diff --git a/Rubberduck.Refactorings/Common/VBAIdentifierValidator.cs b/Rubberduck.Refactorings/Common/VBAIdentifierValidator.cs index bddc7141c7..06b5889867 100644 --- a/Rubberduck.Refactorings/Common/VBAIdentifierValidator.cs +++ b/Rubberduck.Refactorings/Common/VBAIdentifierValidator.cs @@ -12,12 +12,6 @@ namespace Rubberduck.Refactorings.Common { public static class VBAIdentifierValidator { - // NOTE: ForbiddenAttribute marks the tokens that don't compile as identifier names. Includes "bad but legal" names. - // TODO: Compare with the unfiltered tokens if a client needs to tell "bad but legal" from "bad and illegal" names. - private static readonly IEnumerable ReservedIdentifiers = - typeof(Tokens).GetFields() - .Where(item => item.GetType().GetCustomAttributes().Any()) - .Select(item => item.GetValue(null).ToString()).ToArray(); /// /// Predicate function determining if an identifier string's content will trigger a result for the UseMeaningfulNames inspection. @@ -111,7 +105,7 @@ public static bool TryMatchInvalidIdentifierCriteria(string name, DeclarationTyp //Is a reserved identifier if (!declarationType.HasFlag(DeclarationType.UserDefinedTypeMember)) { - if (ReservedIdentifiers.Contains(name, StringComparer.InvariantCultureIgnoreCase)) + if (Tokens.IllegalIdentifierNames.Contains(name, StringComparer.InvariantCultureIgnoreCase)) { criteriaMatchMessage = string.Format(RubberduckUI.InvalidNameCriteria_IsReservedKeywordFormat, name); return true; @@ -124,7 +118,7 @@ public static bool TryMatchInvalidIdentifierCriteria(string name, DeclarationTyp //Name is not a reserved identifier, but when used as a UDTMember array declaration //it collides with the 'Name' Statement (Renames a disk file, directory, or folder) - var invalidUDTArrayIdentifiers = ReservedIdentifiers.Concat(new List() { "Name" }); + var invalidUDTArrayIdentifiers = Tokens.IllegalIdentifierNames.Concat(new List() { "Name" }); if (invalidUDTArrayIdentifiers.Contains(name, StringComparer.InvariantCultureIgnoreCase)) { @@ -182,7 +176,7 @@ public static IReadOnlyList SatisfiedInvalidIdentifierCriteria(string na //Is a reserved identifier if (!declarationType.HasFlag(DeclarationType.UserDefinedTypeMember)) { - if (ReservedIdentifiers.Contains(name, StringComparer.InvariantCultureIgnoreCase)) + if (Tokens.IllegalIdentifierNames.Contains(name, StringComparer.InvariantCultureIgnoreCase)) { criteriaMatchMessages.Add(string.Format(RubberduckUI.InvalidNameCriteria_IsReservedKeywordFormat, name)); } @@ -194,7 +188,7 @@ public static IReadOnlyList SatisfiedInvalidIdentifierCriteria(string na //Name is not a reserved identifier, but when used as a UDTMember array declaration //it collides with the 'Name' Statement (Renames a disk file, directory, or folder) - var invalidUDTArrayIdentifiers = ReservedIdentifiers.Concat(new List() { "Name" }); + var invalidUDTArrayIdentifiers = Tokens.IllegalIdentifierNames.Concat(new List() { "Name" }); if (invalidUDTArrayIdentifiers.Contains(name, StringComparer.InvariantCultureIgnoreCase)) { diff --git a/Rubberduck.Resources/Tokens.cs b/Rubberduck.Resources/Tokens.cs index 0e369f425a..5320476075 100644 --- a/Rubberduck.Resources/Tokens.cs +++ b/Rubberduck.Resources/Tokens.cs @@ -1,11 +1,25 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; namespace Rubberduck.Resources { + /// + /// Identifies a static string that isn't a legal identifier name for user code, e.g. keyword or reserved identifier. + /// + [AttributeUsage(AttributeTargets.Field)] + public class ForbiddenAttribute : Attribute { } + [SuppressMessage("ReSharper", "InconsistentNaming")] public static class Tokens { + public static IEnumerable IllegalIdentifierNames => + typeof(Tokens).GetFields().Where(item => item.GetCustomAttributes().Any()) + .Select(item => item.GetValue(null)).Cast().Select(item => item); + [Forbidden] public static readonly string Abs = "Abs"; [Forbidden] @@ -374,11 +388,4 @@ public static class Tokens public static readonly string XOr = "Xor"; public static readonly string Year = "Year"; } - - /// - /// Identifies a static string that isn't a legal identifier name for user code, e.g. keyword or reserved identifier. - /// - public class ForbiddenAttribute : Attribute - { - } } From fce9740f6884ebf8468952d13c8cdd7072342def Mon Sep 17 00:00:00 2001 From: Mathieu Guindon Date: Fri, 23 Apr 2021 20:09:45 -0400 Subject: [PATCH 9/9] move IMenuItem.CanExecute and commandbar/selection string to async tasks; --- .../Concrete/ParameterNotUsedInspection.cs | 2 +- Rubberduck.Core/AppMenu.cs | 16 +- .../CommandBars/AppCommandBarBase.cs | 38 ++-- .../CommandBars/IContextFormatter.cs | 124 +++++++------ .../CommandBars/RubberduckCommandBar.cs | 164 ++++++++++++++---- .../UI/Command/MenuItems/IAppMenu.cs | 4 +- .../ParentMenus/AnnotateParentMenu.cs | 5 +- .../ParentMenus/CodePaneContextParentMenu.cs | 5 +- .../CodePaneRefactoringsParentMenu.cs | 5 +- .../FormDesignerContextParentMenu.cs | 9 +- .../ParentMenus/NavigateParentMenu.cs | 5 +- .../ParentMenus/ParentMenuItemBase.cs | 49 +++--- .../ProjectWindowContextParentMenu.cs | 5 +- .../ParentMenus/RefactoringsParentMenu.cs | 5 +- .../ParentMenus/RubberduckParentMenu.cs | 5 +- .../ParentMenus/SmartIndenterParentMenu.cs | 5 +- .../MenuItems/ParentMenus/ToolsParentMenu.cs | 5 +- .../ParentMenus/UnitTestingParentMenu.cs | 5 +- .../Command/RunSelectedTestModuleCommand.cs | 4 +- .../DeclarationCaching/DeclarationFinder.cs | 2 +- 20 files changed, 306 insertions(+), 156 deletions(-) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ParameterNotUsedInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ParameterNotUsedInspection.cs index b459869cc9..9a24b6e8e9 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ParameterNotUsedInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ParameterNotUsedInspection.cs @@ -114,7 +114,7 @@ private static bool TryFindParameterIndex(ParameterDeclaration parameter, IParam private static bool ParameterAtIndexIsNotUsed(IParameterizedDeclaration declaration, int parameterIndex) { - var parameter = declaration.Parameters.ElementAtOrDefault(parameterIndex); + var parameter = declaration?.Parameters.ElementAtOrDefault(parameterIndex); return parameter != null && !parameter.References.Any(); } diff --git a/Rubberduck.Core/AppMenu.cs b/Rubberduck.Core/AppMenu.cs index 3d3fb880f9..3a4eaf08d9 100644 --- a/Rubberduck.Core/AppMenu.cs +++ b/Rubberduck.Core/AppMenu.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; using NLog; using Rubberduck.Parsing; using Rubberduck.Parsing.VBA; @@ -75,26 +77,26 @@ private void InitializeRubberduckMenus() _logger.Error(exception); } } - EvaluateCanExecute(_parser.State); + EvaluateCanExecuteAsync(_parser.State, CancellationToken.None).Wait(); } - public void OnSelectedDeclarationChange(object sender, DeclarationChangedEventArgs e) + public async void OnSelectedDeclarationChange(object sender, DeclarationChangedEventArgs e) { - EvaluateCanExecute(_parser.State); + await EvaluateCanExecuteAsync(_parser.State, CancellationToken.None); } - private void OnParserStateChanged(object sender, EventArgs e) + private async void OnParserStateChanged(object sender, EventArgs e) { - EvaluateCanExecute(_parser.State); + await EvaluateCanExecuteAsync(_parser.State, CancellationToken.None); } - public void EvaluateCanExecute(RubberduckParserState state) + public async Task EvaluateCanExecuteAsync(RubberduckParserState state, CancellationToken token) { foreach (var menu in _menus) { try { - menu.EvaluateCanExecute(state); + await menu.EvaluateCanExecuteAsync(state, token); } catch (Exception exception) { diff --git a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/AppCommandBarBase.cs b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/AppCommandBarBase.cs index 94e99bbcc7..e51aac6e93 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/AppCommandBarBase.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/AppCommandBarBase.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; using NLog; using Rubberduck.Parsing.UIContext; using Rubberduck.Parsing.VBA; @@ -140,37 +142,45 @@ private ICommandBarControl InitializeChildControl(ICommandMenuItem item) if (item.Command != null) { - child.Click += child_Click; + child.Click += Child_Click; } return child; } - public void EvaluateCanExecute(RubberduckParserState state) + public async Task EvaluateCanExecuteAsync(RubberduckParserState state, CancellationToken token) { - foreach (var kvp in _items.Where(kv => kv.Key != null && kv.Value != null && !kv.Value.IsWrappingNullReference)) + var usableMenuItems = _items.Where(kv => kv.Key != null && kv.Value != null && !kv.Value.IsWrappingNullReference); + foreach (var kvp in usableMenuItems) { + token.ThrowIfCancellationRequested(); var commandItem = kvp.Key; var canExecute = false; try { - canExecute = commandItem.EvaluateCanExecute(state); - + canExecute = await Task.Run(() => commandItem.EvaluateCanExecute(state), token); } catch (Exception e) { - Logger.Error(e, $"{commandItem?.GetType().Name ?? nameof(ICommandMenuItem)}.EvaluateCanExecute(RubberduckParserState) threw an exception."); + Logger.Error(e, + $"{commandItem?.GetType().Name ?? nameof(ICommandMenuItem)}.EvaluateCanExecute(RubberduckParserState) threw an exception."); } + + token.ThrowIfCancellationRequested(); try { - kvp.Value.IsEnabled = canExecute; - if (commandItem?.HiddenWhenDisabled ?? false) + _uiDispatcher.InvokeAsync(() => { - kvp.Value.IsVisible = canExecute; - } + kvp.Value.IsEnabled = canExecute; + if (commandItem?.HiddenWhenDisabled ?? false) + { + kvp.Value.IsVisible = canExecute; + } + }); } - catch (COMException exception) + catch (Exception exception) { - Logger.Error(exception,$"COMException while trying to set IsEnabled and IsVisible on {commandItem?.GetType().Name ?? nameof(ICommandMenuItem)}"); + Logger.Error(exception, + $"Setting IsEnabled and IsVisible on {commandItem?.GetType().Name ?? nameof(ICommandMenuItem)} threw an exception."); } } } @@ -231,7 +241,7 @@ private void RemoveChildren() { if (!button.IsWrappingNullReference) { - button.Click -= child_Click; + button.Click -= Child_Click; } button.Delete(); button.Dispose(); @@ -244,7 +254,7 @@ private void RemoveChildren() _items.Clear(); } - private void child_Click(object sender, CommandBarButtonClickEventArgs e) + private void Child_Click(object sender, CommandBarButtonClickEventArgs e) { ICommandMenuItem item; try diff --git a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs index f5dce69bae..fb6d90e51a 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/IContextFormatter.cs @@ -1,8 +1,10 @@ +using System.Threading; using Path = System.IO.Path; using Rubberduck.Parsing.Symbols; using Rubberduck.VBEditor.SafeComWrappers.Abstract; using Rubberduck.Resources; using Rubberduck.VBEditor; +using System.Threading.Tasks; namespace Rubberduck.UI.Command.MenuItems.CommandBars { @@ -11,16 +13,16 @@ public interface IContextFormatter /// /// Determines the formatting of the contextual selection caption when a codepane is active. /// - string Format(ICodePane activeCodePane, Declaration declaration); + Task FormatAsync(ICodePane activeCodePane, Declaration declaration, CancellationToken token); /// /// Determines the formatting of the contextual selection caption when a codepane is not active. /// - string Format(Declaration declaration, bool multipleControls); + Task FormatAsync(Declaration declaration, bool multipleControls, CancellationToken token); } public class ContextFormatter : IContextFormatter { - public string Format(ICodePane activeCodePane, Declaration declaration) + public async Task FormatAsync(ICodePane activeCodePane, Declaration declaration, CancellationToken token) { if (activeCodePane == null) { @@ -35,93 +37,105 @@ public string Format(ICodePane activeCodePane, Declaration declaration) var selection = qualifiedSelection.Value; var codePaneSelectionText = selection.Selection.ToString(); - var contextSelectionText = FormatDeclaration(declaration); + var contextSelectionText = await FormatDeclarationAsync(declaration, token); return $"{codePaneSelectionText} | {contextSelectionText}"; } - public string Format(Declaration declaration, bool multipleControls) + public async Task FormatAsync(Declaration declaration, bool multipleControls, CancellationToken token) { if (declaration == null) { return string.Empty; } - + + token.ThrowIfCancellationRequested(); // designer, there is no code pane selection - return FormatDeclaration(declaration, multipleControls); + return await FormatDeclarationAsync(declaration, token, multipleControls); } - private string FormatDeclaration(Declaration declaration, bool multipleControls = false) + private async Task FormatDeclarationAsync(Declaration declaration, CancellationToken token, bool multipleControls = false) { + token.ThrowIfCancellationRequested(); var moduleName = declaration.QualifiedName.QualifiedModuleName; var declarationType = RubberduckUI.ResourceManager.GetString("DeclarationType_" + declaration.DeclarationType, Settings.Settings.Culture); var typeName = TypeName(declaration, multipleControls, declarationType); - var formattedDeclaration = FormattedDeclaration(declaration, typeName, moduleName, declarationType); + var formattedDeclaration = await FormattedDeclarationAsync(declaration, typeName, moduleName, declarationType, token); return formattedDeclaration.Trim(); } - private static string FormattedDeclaration( + private async Task FormattedDeclarationAsync( Declaration declaration, string typeName, QualifiedModuleName moduleName, - string declarationType) + string declarationType, + CancellationToken token) { - if (declaration.ParentDeclaration != null) + return await Task.Run(() => { - if (declaration.ParentDeclaration.DeclarationType.HasFlag(DeclarationType.Member)) + + token.ThrowIfCancellationRequested(); + if (declaration.ParentDeclaration != null) { - // locals, parameters - return $"{declaration.ParentDeclaration.QualifiedName}:{declaration.IdentifierName} {typeName}"; + if (declaration.ParentDeclaration.DeclarationType.HasFlag(DeclarationType.Member)) + { + // locals, parameters + return $"{declaration.ParentDeclaration.QualifiedName}:{declaration.IdentifierName} {typeName}"; + } + + if (declaration.ParentDeclaration.DeclarationType.HasFlag(DeclarationType.Module)) + { + // fields + var withEvents = declaration.IsWithEvents ? $"({Tokens.WithEvents}) " : string.Empty; + return $"{withEvents}{moduleName}.{declaration.IdentifierName} {typeName}"; + } } - if (declaration.ParentDeclaration.DeclarationType.HasFlag(DeclarationType.Module)) + token.ThrowIfCancellationRequested(); + if (declaration.DeclarationType.HasFlag(DeclarationType.Member)) { - // fields - var withEvents = declaration.IsWithEvents ? $"({Tokens.WithEvents}) " : string.Empty; - return $"{withEvents}{moduleName}.{declaration.IdentifierName} {typeName}"; + var formattedDeclaration = $"{declaration.QualifiedName}"; + if (declaration.DeclarationType == DeclarationType.Function + || declaration.DeclarationType == DeclarationType.PropertyGet) + { + formattedDeclaration += $" {typeName}"; + } + + return formattedDeclaration; } - } - if (declaration.DeclarationType.HasFlag(DeclarationType.Member)) - { - var formattedDeclaration = $"{declaration.QualifiedName}"; - if (declaration.DeclarationType == DeclarationType.Function - || declaration.DeclarationType == DeclarationType.PropertyGet) + if (declaration.DeclarationType.HasFlag(DeclarationType.Module)) { - formattedDeclaration += $" {typeName}"; + return $"{moduleName} ({declarationType})"; } - return formattedDeclaration; - } - - if (declaration.DeclarationType.HasFlag(DeclarationType.Module)) - { - return $"{moduleName} ({declarationType})"; - } - - switch (declaration.DeclarationType) - { - case DeclarationType.Project: - case DeclarationType.BracketedExpression: - var filename = Path.GetFileName(declaration.QualifiedName.QualifiedModuleName.ProjectPath); - return $"{filename}{(string.IsNullOrEmpty(filename) ? string.Empty : ";")}{declaration.IdentifierName} ({declarationType})"; - case DeclarationType.Enumeration: - case DeclarationType.UserDefinedType: - return !declaration.IsUserDefined - // built-in enums & UDTs don't have a module - ? $"{Path.GetFileName(moduleName.ProjectPath)};{declaration.IdentifierName}" - : moduleName.ToString(); - case DeclarationType.EnumerationMember: - case DeclarationType.UserDefinedTypeMember: - return declaration.IsUserDefined - ? $"{moduleName}.{declaration.ParentDeclaration.IdentifierName}.{declaration.IdentifierName} {typeName}" - : $"{Path.GetFileName(moduleName.ProjectPath)};{declaration.ParentDeclaration.IdentifierName}.{declaration.IdentifierName} {typeName}"; - case DeclarationType.ComAlias: - return $"{Path.GetFileName(moduleName.ProjectPath)};{declaration.IdentifierName} (alias:{declaration.AsTypeName})"; - } + token.ThrowIfCancellationRequested(); + switch (declaration.DeclarationType) + { + case DeclarationType.Project: + case DeclarationType.BracketedExpression: + var filename = Path.GetFileName(declaration.QualifiedName.QualifiedModuleName.ProjectPath); + return + $"{filename}{(string.IsNullOrEmpty(filename) ? string.Empty : ";")}{declaration.IdentifierName} ({declarationType})"; + case DeclarationType.Enumeration: + case DeclarationType.UserDefinedType: + return !declaration.IsUserDefined + // built-in enums & UDTs don't have a module + ? $"{Path.GetFileName(moduleName.ProjectPath)};{declaration.IdentifierName}" + : moduleName.ToString(); + case DeclarationType.EnumerationMember: + case DeclarationType.UserDefinedTypeMember: + return declaration.IsUserDefined + ? $"{moduleName}.{declaration.ParentDeclaration.IdentifierName}.{declaration.IdentifierName} {typeName}" + : $"{Path.GetFileName(moduleName.ProjectPath)};{declaration.ParentDeclaration.IdentifierName}.{declaration.IdentifierName} {typeName}"; + case DeclarationType.ComAlias: + return + $"{Path.GetFileName(moduleName.ProjectPath)};{declaration.IdentifierName} (alias:{declaration.AsTypeName})"; + } - return string.Empty; + return string.Empty; + }, token); } private static string TypeName(Declaration declaration, bool multipleControls, string declarationType) diff --git a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs index 17a32ddad9..bf08a86932 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; using Rubberduck.Resources; using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.UIContext; @@ -35,13 +37,14 @@ public override void Initialize() { base.Initialize(); SetStatusLabelCaption(ParserState.Pending); - EvaluateCanExecute(_state); + EvaluateCanExecuteAsync(_state, CancellationToken.None); } private Declaration _lastDeclaration; private ParserState _lastStatus = ParserState.None; - private void EvaluateCanExecute(RubberduckParserState state, Declaration selected) + private async Task EvaluateCanExecuteAsync(RubberduckParserState state, Declaration selected, CancellationToken token) { + token.ThrowIfCancellationRequested(); var currentStatus = _state.Status; if (_lastStatus == currentStatus && (selected == null || selected.Equals(_lastDeclaration)) && @@ -52,29 +55,77 @@ private void EvaluateCanExecute(RubberduckParserState state, Declaration selecte _lastStatus = currentStatus; _lastDeclaration = selected; - base.EvaluateCanExecute(state); + await base.EvaluateCanExecuteAsync(state, token); } - private void OnSelectionChange(object sender, DeclarationChangedEventArgs e) + private readonly ConcurrentDictionary _tokenSources = new ConcurrentDictionary(); + + private async void OnSelectionChange(object sender, DeclarationChangedEventArgs e) { - var caption = _formatter.Format(e.Declaration, e.MultipleControlsSelected); - if (string.IsNullOrEmpty(caption)) + try { - //Fallback caption for selections in the Project window. - caption = e.FallbackCaption; - } + try + { + if (_tokenSources.TryRemove(nameof(OnSelectionChange), out var existing)) + { + existing.Cancel(); + } + } + catch (ObjectDisposedException) + { + Logger.Trace($"CancellationTokenSource was already disposed for {nameof(OnSelectionChange)}."); + } + + var source = _tokenSources.GetOrAdd(nameof(OnSelectionChange), k => new CancellationTokenSource()); + var token = source.Token; + + await Task.Run(async () => + { + var caption = await _formatter.FormatAsync(e.Declaration, e.MultipleControlsSelected, token); + token.ThrowIfCancellationRequested(); + + var argRefCount = e.Declaration is ParameterDeclaration parameter ? parameter.ArgumentReferences.Count() : 0; + var refCount = e.Declaration?.References.Count() ?? 0 + argRefCount; + var description = e.Declaration?.DescriptionString ?? string.Empty; + token.ThrowIfCancellationRequested(); - var refCount = (e.Declaration?.References.Count() ?? 0) - + ((e.Declaration is ParameterDeclaration parameter) - ? parameter.ArgumentReferences.Count() - : 0); - var description = e.Declaration?.DescriptionString ?? string.Empty; - //& renders the next character as if it was an accelerator. - SetContextSelectionCaption(caption?.Replace("&", "&&"), refCount, description); - EvaluateCanExecute(_state, e.Declaration); + //& renders the next character as if it was an accelerator. + SetContextSelectionCaption(caption?.Replace("&", "&&"), refCount, description); + token.ThrowIfCancellationRequested(); + + await EvaluateCanExecuteAsync(_state, e.Declaration, token); + + }, token) + .ContinueWith(t => + { + try + { + if (!t.IsCanceled) + { + source.Dispose(); + } + } + catch (Exception exception) + { + Logger.Trace($"CancellationTokenSource.Dispose() threw an exception for {nameof(OnSelectionChange)}: {exception}"); + } + }, token); + } + catch(ObjectDisposedException) + { + Logger.Trace($"CancellationTokenSource was already disposed for {nameof(OnSelectionChange)}."); + } + catch (OperationCanceledException exception) + { + Logger.Info(exception); + } + catch (Exception exception) + { + Logger.Error(exception); + } } - + private void OnParserStatusMessageUpdate(object sender, RubberduckStatusMessageEventArgs e) { var message = e.Message; @@ -87,11 +138,56 @@ private void OnParserStatusMessageUpdate(object sender, RubberduckStatusMessageE SetStatusLabelCaption(message, _state.ModuleExceptions.Count); } - private void OnParserStateChanged(object sender, EventArgs e) + private async void OnParserStateChanged(object sender, EventArgs e) { - _lastStatus = _state.Status; - EvaluateCanExecute(_state); - SetStatusLabelCaption(_state.Status, _state.ModuleExceptions.Count); + try + { + _lastStatus = _state.Status; + try + { + if (_tokenSources.TryRemove(nameof(OnParserStateChanged), out var existing)) + { + existing.Cancel(); + } + } + catch (ObjectDisposedException) + { + Logger.Trace($"CancellationTokenSource was already disposed for {nameof(OnParserStateChanged)}."); + } + + var source = _tokenSources.GetOrAdd(nameof(OnParserStateChanged), k => new CancellationTokenSource()); + var token = source.Token; + + await EvaluateCanExecuteAsync(_state, token) + .ContinueWith(t => + { + try + { + if (!t.IsCanceled) + { + source.Dispose(); + } + } + catch (Exception exception) + { + Logger.Trace($"CancellationTokenSource.Dispose() threw an exception for {nameof(OnParserStateChanged)}: {exception}"); + } + }, token); + + SetStatusLabelCaption(_state.Status, _state.ModuleExceptions.Count); + } + catch (ObjectDisposedException) + { + Logger.Trace($"CancellationTokenSource was already disposed for {nameof(OnParserStateChanged)}."); + } + catch (OperationCanceledException exception) + { + Logger.Info(exception); + } + catch (Exception exception) + { + Logger.Error(exception); + } } public void SetStatusLabelCaption(ParserState state, int? errorCount = null) @@ -102,16 +198,12 @@ public void SetStatusLabelCaption(ParserState state, int? errorCount = null) private void SetStatusLabelCaption(string caption, int? errorCount = null) { - var reparseCommandButton = - FindChildByTag(typeof(ReparseCommandMenuItem).FullName) as ReparseCommandMenuItem; - if (reparseCommandButton == null) + if (!(FindChildByTag(typeof(ReparseCommandMenuItem).FullName) is ReparseCommandMenuItem reparseCommandButton)) { return; } - var showErrorsCommandButton = - FindChildByTag(typeof(ShowParserErrorsCommandMenuItem).FullName) as ShowParserErrorsCommandMenuItem; - if (showErrorsCommandButton == null) + if (!(FindChildByTag(typeof(ShowParserErrorsCommandMenuItem).FullName) is ShowParserErrorsCommandMenuItem showErrorsCommandButton)) { return; } @@ -122,7 +214,7 @@ private void SetStatusLabelCaption(string caption, int? errorCount = null) { reparseCommandButton.SetCaption(caption); reparseCommandButton.SetToolTip(string.Format(RubberduckUI.ReparseToolTipText, caption)); - if (errorCount.HasValue && errorCount.Value > 0) + if (errorCount > 0) { showErrorsCommandButton.SetToolTip( string.Format(RubberduckUI.ParserErrorToolTipText, errorCount.Value)); @@ -173,6 +265,18 @@ protected virtual void Dispose(bool disposing) return; } + foreach (var source in _tokenSources) + { + try + { + source.Value.Dispose(); + } + catch (Exception exception) + { + Logger.Trace($"Disposing CancellationTokenSource for {nameof(OnParserStateChanged)} threw an exception: {exception}"); + } + } + _selectionService.SelectionChanged -= OnSelectionChange; _state.StateChangedHighPriority -= OnParserStateChanged; _state.StatusMessageUpdate -= OnParserStatusMessageUpdate; diff --git a/Rubberduck.Core/UI/Command/MenuItems/IAppMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/IAppMenu.cs index 2be78c288e..801ce7bc8d 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/IAppMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/IAppMenu.cs @@ -1,3 +1,5 @@ +using System.Threading; +using System.Threading.Tasks; using Rubberduck.Parsing.VBA; namespace Rubberduck.UI.Command.MenuItems @@ -6,6 +8,6 @@ public interface IAppMenu { void Localize(); void Initialize(); - void EvaluateCanExecute(RubberduckParserState state); + Task EvaluateCanExecuteAsync(RubberduckParserState state, CancellationToken token); } } diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/AnnotateParentMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/AnnotateParentMenu.cs index cdda49240d..c76d9f615d 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/AnnotateParentMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/AnnotateParentMenu.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using Rubberduck.Parsing.UIContext; namespace Rubberduck.UI.Command.MenuItems.ParentMenus { public class AnnotateParentMenu : ParentMenuItemBase { - public AnnotateParentMenu(IEnumerable items) - : base("AnnotateMenu", items) + public AnnotateParentMenu(IEnumerable items, IUiDispatcher dispatcher) + : base(dispatcher, "AnnotateMenu", items) { } diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/CodePaneContextParentMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/CodePaneContextParentMenu.cs index 97068d6c43..41109510ac 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/CodePaneContextParentMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/CodePaneContextParentMenu.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using Rubberduck.Parsing.UIContext; namespace Rubberduck.UI.Command.MenuItems.ParentMenus { public class CodePaneContextParentMenu : ParentMenuItemBase { - public CodePaneContextParentMenu(IEnumerable items, int beforeIndex) - : base("RubberduckMenu", items, beforeIndex) + public CodePaneContextParentMenu(IEnumerable items, int beforeIndex, IUiDispatcher dispatcher) + : base(dispatcher,"RubberduckMenu", items, beforeIndex) { } diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/CodePaneRefactoringsParentMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/CodePaneRefactoringsParentMenu.cs index d2cf0ba2f6..9153b7ee23 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/CodePaneRefactoringsParentMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/CodePaneRefactoringsParentMenu.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using Rubberduck.Parsing.UIContext; namespace Rubberduck.UI.Command.MenuItems.ParentMenus { public class CodePaneRefactoringsParentMenu : ParentMenuItemBase { - public CodePaneRefactoringsParentMenu(IEnumerable items) - : base("RubberduckMenu_CodePaneRefactor", items) + public CodePaneRefactoringsParentMenu(IEnumerable items, IUiDispatcher dispatcher) + : base(dispatcher, "RubberduckMenu_CodePaneRefactor", items) { } //This display order is different from the main menu; it is the sole reason this class is separate from the main menu one. diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/FormDesignerContextParentMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/FormDesignerContextParentMenu.cs index 3731a6829e..6042df22cb 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/FormDesignerContextParentMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/FormDesignerContextParentMenu.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using Rubberduck.Parsing.UIContext; namespace Rubberduck.UI.Command.MenuItems.ParentMenus { public class FormDesignerContextParentMenu : ParentMenuItemBase { - public FormDesignerContextParentMenu(IEnumerable items, int beforeIndex) - : base("RubberduckMenu", items, beforeIndex) + public FormDesignerContextParentMenu(IEnumerable items, int beforeIndex, IUiDispatcher dispatcher) + : base(dispatcher,"RubberduckMenu", items, beforeIndex) { } @@ -14,8 +15,8 @@ public FormDesignerContextParentMenu(IEnumerable items, int beforeInd public class FormDesignerControlContextParentMenu : ParentMenuItemBase { - public FormDesignerControlContextParentMenu(IEnumerable items, int beforeIndex) - : base("RubberduckMenu", items, beforeIndex) + public FormDesignerControlContextParentMenu(IEnumerable items, int beforeIndex, IUiDispatcher dispatcher) + : base(dispatcher,"RubberduckMenu", items, beforeIndex) { } diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/NavigateParentMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/NavigateParentMenu.cs index 1dbbf93bbb..53b38c2488 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/NavigateParentMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/NavigateParentMenu.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using Rubberduck.Parsing.UIContext; namespace Rubberduck.UI.Command.MenuItems.ParentMenus { public class NavigateParentMenu : ParentMenuItemBase { - public NavigateParentMenu(IEnumerable items) - : base("RubberduckMenu_Navigate", items) + public NavigateParentMenu(IEnumerable items, IUiDispatcher dispatcher) + : base(dispatcher,"RubberduckMenu_Navigate", items) { } diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs index 9e231ebb4e..7e5c9a3123 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs @@ -2,9 +2,13 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Rubberduck.Parsing.VBA; using NLog; +using Rubberduck.Parsing.UIContext; using Rubberduck.Resources.Menus; +using Rubberduck.VBEditor.Extensions; using Rubberduck.VBEditor.SafeComWrappers; using Rubberduck.VBEditor.SafeComWrappers.Abstract; @@ -16,12 +20,14 @@ public abstract class ParentMenuItemBase : IParentMenuItem private readonly int? _beforeIndex; private readonly IDictionary _items; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + protected readonly IUiDispatcher _uiDispatcher; - protected ParentMenuItemBase(string key, IEnumerable items, int? beforeIndex = null) + protected ParentMenuItemBase(IUiDispatcher dispatcher, string key, IEnumerable items, int? beforeIndex = null) { _key = key; _beforeIndex = beforeIndex; _items = items.ToDictionary(item => item, item => null as ICommandBarControl); + _uiDispatcher = dispatcher; } private ICommandBarControls _parent; @@ -102,7 +108,7 @@ public void Initialize() ?? InitializeChildControl(item as IParentMenuItem); } - EvaluateCanExecute(null); + EvaluateCanExecuteAsync(null, CancellationToken.None).Wait(); } public void RemoveMenu() @@ -130,28 +136,29 @@ private void RemoveChildren() } } - public void EvaluateCanExecute(RubberduckParserState state) + public async Task EvaluateCanExecuteAsync(RubberduckParserState state, CancellationToken token) { - foreach (var kvp in _items) + foreach (var (key, value) in _items) { - if (kvp.Key is IParentMenuItem parentItem) - { - parentItem.EvaluateCanExecute(state); - continue; - } - - if (kvp.Key is ICommandMenuItem commandItem && kvp.Value != null) + switch (key) { - try - { - kvp.Value.IsEnabled = commandItem.EvaluateCanExecute(state); - } - catch (Exception exception) - { - kvp.Value.IsEnabled = false; - Logger.Error(exception, "Could not evaluate availability of commmand menu item {0}.", kvp.Value.Tag ?? "{Unknown}"); - } - + case IParentMenuItem parentItem: + await parentItem.EvaluateCanExecuteAsync(state, token); + continue; + case ICommandMenuItem commandItem when value != null: + _uiDispatcher.InvokeAsync(() => + { + try + { + value.IsEnabled = commandItem.EvaluateCanExecute(state); + } + catch (Exception exception) + { + value.IsEnabled = false; + Logger.Error(exception, "Could not evaluate availability of commmand menu item {0}.", value.Tag ?? "{Unknown}"); + } + }); + break; } } } diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ProjectWindowContextParentMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ProjectWindowContextParentMenu.cs index c314ca1960..c0fa82f1a1 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ProjectWindowContextParentMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ProjectWindowContextParentMenu.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using Rubberduck.Parsing.UIContext; namespace Rubberduck.UI.Command.MenuItems.ParentMenus { public class ProjectWindowContextParentMenu : ParentMenuItemBase { - public ProjectWindowContextParentMenu(IEnumerable items, int beforeIndex) - : base("RubberduckMenu", items, beforeIndex) + public ProjectWindowContextParentMenu(IEnumerable items, int beforeIndex, IUiDispatcher dispatcher) + : base(dispatcher,"RubberduckMenu", items, beforeIndex) { } diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/RefactoringsParentMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/RefactoringsParentMenu.cs index 30b2d6951c..c0bcf2789e 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/RefactoringsParentMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/RefactoringsParentMenu.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using Rubberduck.Parsing.UIContext; namespace Rubberduck.UI.Command.MenuItems.ParentMenus { public class RefactoringsParentMenu : ParentMenuItemBase { - public RefactoringsParentMenu(IEnumerable items) - : base("RubberduckMenu_Refactor", items) + public RefactoringsParentMenu(IEnumerable items, IUiDispatcher dispatcher) + : base(dispatcher,"RubberduckMenu_Refactor", items) { } diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/RubberduckParentMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/RubberduckParentMenu.cs index 0cf161bbc5..0f939568b9 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/RubberduckParentMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/RubberduckParentMenu.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using Rubberduck.Parsing.UIContext; namespace Rubberduck.UI.Command.MenuItems.ParentMenus { public class RubberduckParentMenu : ParentMenuItemBase { - public RubberduckParentMenu(IEnumerable items, int beforeIndex) - : base("RubberduckMenu", items, beforeIndex) + public RubberduckParentMenu(IEnumerable items, int beforeIndex, IUiDispatcher dispatcher) + : base(dispatcher, "RubberduckMenu", items, beforeIndex) { } } diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/SmartIndenterParentMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/SmartIndenterParentMenu.cs index 01c988f116..f5df5702d9 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/SmartIndenterParentMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/SmartIndenterParentMenu.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using Rubberduck.Parsing.UIContext; namespace Rubberduck.UI.Command.MenuItems.ParentMenus { public class SmartIndenterParentMenu : ParentMenuItemBase { - public SmartIndenterParentMenu(IEnumerable items) - : base("SmartIndenterMenu", items) + public SmartIndenterParentMenu(IEnumerable items, IUiDispatcher dispatcher) + : base(dispatcher, "SmartIndenterMenu", items) { } diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ToolsParentMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ToolsParentMenu.cs index e773f14fc9..0539a8ff7a 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ToolsParentMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/ToolsParentMenu.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using Rubberduck.Parsing.UIContext; namespace Rubberduck.UI.Command.MenuItems.ParentMenus { public class ToolsParentMenu : ParentMenuItemBase { - public ToolsParentMenu(IEnumerable items) - : base("ToolsMenu", items) + public ToolsParentMenu(IEnumerable items, IUiDispatcher dispatcher) + : base(dispatcher, "ToolsMenu", items) { } diff --git a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/UnitTestingParentMenu.cs b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/UnitTestingParentMenu.cs index 3637b538ed..d496ba4fa8 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/UnitTestingParentMenu.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/ParentMenus/UnitTestingParentMenu.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using Rubberduck.Parsing.UIContext; namespace Rubberduck.UI.Command.MenuItems.ParentMenus { public class UnitTestingParentMenu : ParentMenuItemBase { - public UnitTestingParentMenu(IEnumerable items) - : base("RubberduckMenu_UnitTests", items) + public UnitTestingParentMenu(IEnumerable items, IUiDispatcher dispatcher) + : base(dispatcher, "RubberduckMenu_UnitTests", items) { } diff --git a/Rubberduck.Core/UI/Command/RunSelectedTestModuleCommand.cs b/Rubberduck.Core/UI/Command/RunSelectedTestModuleCommand.cs index 217c98de83..367d5975ab 100644 --- a/Rubberduck.Core/UI/Command/RunSelectedTestModuleCommand.cs +++ b/Rubberduck.Core/UI/Command/RunSelectedTestModuleCommand.cs @@ -23,7 +23,7 @@ private bool SpecialEvaluateCanExecute(object parameter) { return (parameter ?? FindModuleFromSelection()) is Declaration candidate && candidate.DeclarationType == DeclarationType.ProceduralModule && - candidate.Annotations.Any(annotation => annotation is TestModuleAnnotation) && + candidate.Annotations.Any(annotation => annotation.Annotation is TestModuleAnnotation) && _engine.CanRun && _engine.Tests.Any(test => test.Declaration.QualifiedModuleName.Equals(candidate.QualifiedModuleName)); } @@ -32,7 +32,7 @@ protected override void OnExecute(object parameter) { if (!((parameter ?? FindModuleFromSelection()) is Declaration candidate) || candidate.DeclarationType != DeclarationType.ProceduralModule || - !candidate.Annotations.Any(annotation => annotation is TestModuleAnnotation) || + !candidate.Annotations.Any(annotation => annotation.Annotation is TestModuleAnnotation) || !_engine.CanRun) { return; diff --git a/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs b/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs index 38261831c7..ddc50cbd21 100644 --- a/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs +++ b/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs @@ -655,7 +655,7 @@ public ParameterDeclaration FindParameterOfNonDefaultMemberFromSimpleArgumentNot } parameter = parameters .Select((param, index) => (param, index)) - .Single(item => item.index == indexedArg.index).param; + .SingleOrDefault(item => item.index == indexedArg.index).param; } return parameter;