Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing 'Peek Definition' commands #5751

Merged
merged 17 commits into from
May 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerViewModel.cs
Original file line number Diff line number Diff line change
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 CodeExplorerViewModel(
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
Original file line number Diff line number Diff line change
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>
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,11 @@ protected CodeExplorerMoveToFolderCommandBase(
_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;
retailcoder marked this conversation as resolved.
Show resolved Hide resolved

protected abstract ICodeExplorerNode NodeFromParameter(object parameter);
protected abstract MoveMultipleFoldersModel ModifiedFolderModel(MoveMultipleFoldersModel model, object parameter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,10 @@ protected CodeExplorerRefactoringCommandBase(

_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
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ protected AddComponentCommandBase(
_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
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ public AddMDIFormCommand(
{
_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
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public AddTemplateCommand(
_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
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ public AnnotateDeclarationCommand(
_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
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,21 @@ public CodeExplorerExtractInterfaceCommand(
_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
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ public CodeExplorerFindAllImplementationsCommand(
_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
Original file line number Diff line number Diff line change
Expand Up @@ -31,45 +31,68 @@ public CodeExplorerFindAllReferencesCommand(
_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;
}
}
}
Loading