Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #5751 from retailcoder/peek
Introduces 'Peek Definition' commands, fixes 'Find All Implementations' target acquisition when used from the VBE's *Project Explorer* toolwindow.
  • Loading branch information
retailcoder committed May 8, 2021
2 parents 2026e69 + 80581fb commit 18f9597
Show file tree
Hide file tree
Showing 62 changed files with 892 additions and 146 deletions.
70 changes: 69 additions & 1 deletion Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerViewModel.cs
Expand Up @@ -3,6 +3,7 @@
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using NLog;
using Rubberduck.Parsing.Symbols;
using Rubberduck.Parsing.VBA;
Expand All @@ -19,6 +20,7 @@
using Rubberduck.Templates;
using Rubberduck.UI.CodeExplorer.Commands.DragAndDrop;
using Rubberduck.UI.Command.ComCommands;
using Rubberduck.UI.Controls;
using Rubberduck.UI.UnitTesting.ComCommands;
using Rubberduck.VBEditor.SafeComWrappers.Abstract;

Expand All @@ -35,8 +37,13 @@ public enum CodeExplorerSortOrder
DeclarationTypeThenCodeLine = DeclarationType | CodeLine
}

public interface IPeekDefinitionPopupProvider
{
void PeekDefinition(Declaration target);
}

[SuppressMessage("ReSharper", "InconsistentNaming")]
public sealed class CodeExplorerViewModel : ViewModelBase
public sealed class CodeExplorerViewModel : ViewModelBase, IPeekDefinitionPopupProvider
{
// ReSharper disable NotAccessedField.Local - The settings providers aren't used, but several enhancement requests will need them.
#pragma warning disable IDE0052 // Remove unread private members
Expand All @@ -53,6 +60,7 @@ public sealed class CodeExplorerViewModel : ViewModelBase
public CodeExplorerViewModel(
RubberduckParserState state,
RemoveCommand removeCommand,
PeekDefinitionNavigateCommand peekNavigateCommand,
IConfigurationService<GeneralSettings> generalSettingsProvider,
IConfigurationService<WindowSettings> windowSettingsProvider,
IUiDispatcher uiDispatcher,
Expand Down Expand Up @@ -86,6 +94,8 @@ public sealed class CodeExplorerViewModel : ViewModelBase
RemoveCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteRemoveCommand, _externalRemoveCommand.CanExecute);
}

PeekDefinitionCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecutePeekDefinitionCommand, CanExecutePeekDefinitionCommand);
ClosePeekDefinitionCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteClosePeekDefinitionCommand);

OnPropertyChanged(nameof(Projects));

Expand Down Expand Up @@ -445,6 +455,61 @@ private void ExecuteRemoveCommand(object param)
public CommandBase CollapseAllCommand { get; }
public CommandBase ExpandAllCommand { get; }

public CommandBase PeekDefinitionCommand { get; }
public CommandBase ClosePeekDefinitionCommand { get; }
public PeekDefinitionFindAllReferencesCommand PeekFindReferencesCommand { get; set; }
public PeekDefinitionNavigateCommand PeekNavigateCommand { get; set; }

private bool _showPeekDefinitionPopup;
public bool ShowPeekDefinitionPopup
{
get => _showPeekDefinitionPopup;
set
{
if (value != _showPeekDefinitionPopup)
{
_showPeekDefinitionPopup = value;
OnPropertyChanged();
}
}
}

private PeekDefinitionViewModel _peekDefinitionViewModel;
public PeekDefinitionViewModel PeekDefinitionViewModel
{
get => _peekDefinitionViewModel;
private set
{
if (_peekDefinitionViewModel != value)
{
_peekDefinitionViewModel = value;
OnPropertyChanged();
}
}
}

private void ExecutePeekDefinitionCommand(object param)
{
if (param is ICodeExplorerNode node)
{
PeekDefinitionViewModel = new PeekDefinitionViewModel(node, PeekFindReferencesCommand, PeekNavigateCommand, ClosePeekDefinitionCommand, _state);
}
else if (param is Declaration declaration)
{
PeekDefinitionViewModel = new PeekDefinitionViewModel(declaration, PeekFindReferencesCommand, PeekNavigateCommand, ClosePeekDefinitionCommand, _state);
}
else
{
PeekDefinitionViewModel = null;
}

ShowPeekDefinitionPopup = PeekDefinitionViewModel != null;
}

private void ExecuteClosePeekDefinitionCommand(object param) => ShowPeekDefinitionPopup = false;

private bool CanExecutePeekDefinitionCommand(object param) => param is Declaration || SelectedItem is CodeExplorerMemberViewModel || SelectedItem is CodeExplorerComponentViewModel;

public ICodeExplorerNode FindVisibleNodeForDeclaration(Declaration declaration)
{
if (declaration == null)
Expand Down Expand Up @@ -541,5 +606,8 @@ public void Dispose()
_generalSettingsProvider.SettingsChanged -= GeneralSettingsChanged;
}
}

// IPeekDefinitionPopupProvider.PeekDefinition
public void PeekDefinition(Declaration target) => ExecutePeekDefinitionCommand(target);
}
}
13 changes: 12 additions & 1 deletion Rubberduck.Core/UI/CodeExplorer/CodeExplorerControl.xaml
Expand Up @@ -59,7 +59,7 @@
<converters:AnnotateDeclarationCommandParameterToTupleConverter x:Key="AnnotateDeclarationCommandParameterToTuple" />
<converters:AnnotateDeclarationCommandCEVisibilityConverter x:Key="AnnotateDeclarationCommandVisibility" />
<converters:DeclarationToDeclarationTypeStringConverter x:Key="DeclarationTypeNameConverter" />

<CompositeCollection x:Key="AddModuleCommands" x:Shared="False">
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.CodeExplorer.CodeExplorerUI, Key=CodeExplorer_AddExistingFileText}"
Command="{Binding ImportCommand}"
Expand Down Expand Up @@ -563,6 +563,9 @@
Command="{Binding FindAllImplementationsCommand}"
CommandParameter="{Binding SelectedItem, Mode=OneWay}" />
</MenuItem>
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=PeekDefinitionCommandText}"
Command="{Binding PeekDefinitionCommand}"
CommandParameter="{Binding SelectedItem, Mode=OneWay}" />
<MenuItem Header="{Resx ResxName=Rubberduck.Resources.CodeExplorer.CodeExplorerUI, Key=CodeExplorer_Indent}"
Command="{Binding IndenterCommand}"
CommandParameter="{Binding SelectedItem, Mode=OneWay}" />
Expand Down Expand Up @@ -625,6 +628,7 @@
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>

<i:Interaction.Behaviors>
<controls:BindableSelectedItemBehavior SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />
</i:Interaction.Behaviors>
Expand Down Expand Up @@ -677,5 +681,12 @@
</WrapPanel>
</ScrollViewer>
</Border>

<Popup x:Name="PeekDefPopup"
Placement="MousePoint"
IsOpen="{Binding ShowPeekDefinitionPopup, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
StaysOpen="True">
<controls:PeekControl DataContext="{Binding PeekDefinitionViewModel, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</Popup>
</Grid>
</UserControl>
Expand Up @@ -46,13 +46,11 @@ public abstract class CodeExplorerMoveToFolderCommandBase : CodeExplorerCommandB
_state = state;
_failureNotifier = failureNotifier;

AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
AddToCanExecuteEvaluation(EvaluateCanExecute);
}

private bool SpecialEvaluateCanExecute(object parameter)
{
return _parserStatusProvider.Status == ParserState.Ready;
}
private bool EvaluateCanExecute(object parameter) =>
_parserStatusProvider.Status == ParserState.Ready;

protected abstract ICodeExplorerNode NodeFromParameter(object parameter);
protected abstract MoveMultipleFoldersModel ModifiedFolderModel(MoveMultipleFoldersModel model, object parameter);
Expand Down
Expand Up @@ -26,13 +26,10 @@ public abstract class CodeExplorerRefactoringCommandBase<TModel> : CodeExplorerC

_parserStatusProvider = parserStatusProvider;

AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
AddToCanExecuteEvaluation(EvaluateCanExecute);
}

private bool SpecialEvaluateCanExecute(object parameter)
{
return _parserStatusProvider.Status == ParserState.Ready;
}
private bool EvaluateCanExecute(object parameter) => _parserStatusProvider.Status == ParserState.Ready;

protected abstract TModel ModelFromParameter(object parameter);
protected abstract void ValidateModel(TModel model);
Expand Down
Expand Up @@ -31,7 +31,7 @@ public abstract class AddComponentCommandBase : CodeExplorerCommandBase
_addComponentService = addComponentService;
_projectsProvider = projectsProvider;

AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
AddToCanExecuteEvaluation(EvaluateCanExecuteBase);
}

public sealed override IEnumerable<Type> ApplicableNodeTypes => ApplicableNodes;
Expand All @@ -45,7 +45,7 @@ protected override void OnExecute(object parameter)
AddComponent(parameter as CodeExplorerItemViewModel);
}

private bool SpecialEvaluateCanExecute(object parameter)
private bool EvaluateCanExecuteBase(object parameter)
{
if (!(parameter is CodeExplorerItemViewModel node)
|| node.Declaration == null)
Expand Down
4 changes: 2 additions & 2 deletions Rubberduck.Core/UI/CodeExplorer/Commands/AddMDIFormCommand.cs
Expand Up @@ -23,14 +23,14 @@ public class AddMDIFormCommand : AddComponentCommandBase
{
_projectsProvider = projectsProvider;

AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
AddToCanExecuteEvaluation(EvaluateCanExecute);
}

public override IEnumerable<ProjectType> AllowableProjectTypes => Types;

public override ComponentType ComponentType => ComponentType.MDIForm;

private bool SpecialEvaluateCanExecute(object parameter)
private bool EvaluateCanExecute(object parameter)
{
if (!(parameter is CodeExplorerItemViewModel node))
{
Expand Down
Expand Up @@ -46,7 +46,7 @@ public class AddTemplateCommand : CodeExplorerCommandBase
_projectsProvider = projectsProvider;
_messageBox = messageBox;

AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
AddToCanExecuteEvaluation(EvaluateCanExecute);
}

public override IEnumerable<Type> ApplicableNodeTypes => new[]{typeof(System.ValueTuple<string, ICodeExplorerNode>)};
Expand All @@ -62,7 +62,7 @@ public bool CanExecuteForNode(ICodeExplorerNode model)
return EvaluateCanExecute(model);
}

private bool SpecialEvaluateCanExecute(object parameter)
private bool EvaluateCanExecute(object parameter)
{
if(parameter is ValueTuple<string, ICodeExplorerNode> data)
{
Expand Down
Expand Up @@ -40,12 +40,12 @@ public class AnnotateDeclarationCommand : CodeExplorerCommandBase
_userInteraction = userInteraction;
_state = state;

AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
AddToCanExecuteEvaluation(EvaluateCanExecute);
}

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

private bool SpecialEvaluateCanExecute(object parameter)
private bool EvaluateCanExecute(object parameter)
{
if (parameter is System.ValueTuple<IAnnotation, ICodeExplorerNode> data)
{
Expand Down
Expand Up @@ -30,20 +30,21 @@ public class CodeExplorerExtractInterfaceCommand : CodeExplorerCommandBase
_state = state;
_refactoring = refactoring;
_failureNotifier = failureNotifier;
AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
AddToOnExecuteEvaluation(FurtherCanExecuteEvaluation);
AddToCanExecuteEvaluation(EvaluateCanExecute);
AddToOnExecuteEvaluation(EvaluateWillExecute);
}

public sealed override IEnumerable<Type> ApplicableNodeTypes => ApplicableNodes;

private bool SpecialEvaluateCanExecute(object parameter)
private bool EvaluateCanExecute(object parameter)
{
return _state.Status == ParserState.Ready
&& parameter is CodeExplorerComponentViewModel node
&& node.QualifiedSelection.HasValue
&& _refactoring.CanExecute(_state, node.QualifiedSelection.Value.QualifiedName);
}

private bool FurtherCanExecuteEvaluation(object parameter)
private bool EvaluateWillExecute(object parameter)
{
return _state.Status == ParserState.Ready
&& parameter is CodeExplorerItemViewModel node
Expand Down
Expand Up @@ -27,12 +27,12 @@ public class CodeExplorerFindAllImplementationsCommand : CodeExplorerCommandBase
_state = state;
_finder = finder;

AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
AddToCanExecuteEvaluation(EvaluateCanExecute);
}

public sealed override IEnumerable<Type> ApplicableNodeTypes => ApplicableNodes;

private bool SpecialEvaluateCanExecute(object parameter)
private bool EvaluateCanExecute(object parameter)
{
return _state.Status == ParserState.Ready &&
parameter is CodeExplorerItemViewModel node &&
Expand Down
Expand Up @@ -31,45 +31,68 @@ public class CodeExplorerFindAllReferencesCommand : CodeExplorerCommandBase
_state = state;
_finder = finder;

AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
AddToCanExecuteEvaluation(EvaluateCanExecute, true);
}

private bool SpecialEvaluateCanExecute(object parameter)
private bool EvaluateCanExecute(object parameter)
{
return _state.Status == ParserState.Ready
&& ((ICodeExplorerNode)parameter).Declaration != null
&& !(parameter is CodeExplorerReferenceViewModel reference
&& reference.IsDimmed);
switch (parameter)
{
case CodeExplorerReferenceViewModel refNode:
return refNode.IsDimmed;
case ICodeExplorerNode node:
return !(node is CodeExplorerCustomFolderViewModel)
&& !(node is CodeExplorerReferenceFolderViewModel);
case Declaration declaration:
return !(declaration is ProjectDeclaration);
default:
return false;
}
}

protected override void OnExecute(object parameter)
{
if (_state.Status != ParserState.Ready
|| !(parameter is ICodeExplorerNode node)
|| node.Declaration == null)
var node = parameter as ICodeExplorerNode;
var declaration = parameter as Declaration;
var reference = parameter as CodeExplorerReferenceViewModel;

if (_state.Status != ParserState.Ready || node == null && declaration == null)
{
return;
}

if (!(node.Parent.Declaration is ProjectDeclaration projectDeclaration))
if (declaration != null)
{
Logger.Error($"The specified ICodeExplorerNode expected to be a direct child of a node whose declaration is a ProjectDeclaration.");
// command could have been invoked from PeekReferences code explorer popup
_finder.FindAllReferences(declaration);
return;
}

if (parameter is CodeExplorerReferenceViewModel reference)
if (reference != null)
{
if (!(reference.Reference is ReferenceModel model))
if (!(node.Parent.Declaration is ProjectDeclaration))
{
Logger.Error(
$"The specified ICodeExplorerNode ({node.GetType()}) is expected to be a direct child of a node whose declaration is a ProjectDeclaration.");
return;
}

if(node.Parent?.Declaration is ProjectDeclaration projectDeclaration)
{
if (!(reference.Reference is ReferenceModel model))
{
Logger.Warn($"Project reference '{reference.Name}' does not have an explorable reference model ({nameof(CodeExplorerReferenceViewModel)}.{nameof(CodeExplorerReferenceViewModel.Reference)} is null.");
return;
}

_finder.FindAllReferences(projectDeclaration, model.ToReferenceInfo());
return;
}
_finder.FindAllReferences(projectDeclaration, model.ToReferenceInfo());
return;
}

_finder.FindAllReferences(node.Declaration);
}

public override IEnumerable<Type> ApplicableNodeTypes => ApplicableNodes;
}
}
}

0 comments on commit 18f9597

Please sign in to comment.