patterncsharpMinor
Code Explorer Commands
Viewed 0 times
codeexplorercommands
Problem
Rubberduck's Code Explorer was recently redesigned from scratch:
Anything from modern features, such as virtual folders (limitation of the VBE--it doesn't support real folders), to ancient features in the original VBE, such as printing.
Here are some of the commands I wrote that I'd like to get reviewed:
The Indent command. This command calls our Smart Indenter port and works on any node. On member nodes, procedures are indented (nothing happens if the node is a field); for component nodes, the selected component is indented; on folders, all components in the folder are indented; and on project nodes, all components in the project are indented.
```
public class CodeExplorer_IndentCommand : CommandBase
{
private readonly RubberduckParserState _state;
private readonly IIndenter _indenter;
private readonly INavigateCommand _navigateCommand;
public CodeExplorer_IndentCommand(RubberduckParserState state, IIndenter indenter, INavigateCommand navigateCommand)
{
_state = state;
_indenter = indenter;
_navigateCommand = navigateCommand;
}
public override bool CanExecute(object parameter)
{
if (parameter is CodeExplorerComponentViewModel)
{
var node = (CodeExplorerComponentViewModel)parameter;
if (node.Declaration.Annotations.Any(a => a.AnnotationType == AnnotationType.NoIndent))
{
return false;
}
}
if (parameter is CodeExplorerProjectViewModel)
{
if (_state.Status != ParserState.Ready)
{
return false;
}
var declaration = ((ICodeExplorerDeclarationViewModel)parameter).Declaration;
return _state.AllUserDeclarations
.Any(c => c.DeclarationType.HasFlag(DeclarationType.Module) &&
c.Annotations.All(a => a.AnnotationType != AnnotationType.NoIndent) &&
c.Project == declar
Anything from modern features, such as virtual folders (limitation of the VBE--it doesn't support real folders), to ancient features in the original VBE, such as printing.
Here are some of the commands I wrote that I'd like to get reviewed:
The Indent command. This command calls our Smart Indenter port and works on any node. On member nodes, procedures are indented (nothing happens if the node is a field); for component nodes, the selected component is indented; on folders, all components in the folder are indented; and on project nodes, all components in the project are indented.
```
public class CodeExplorer_IndentCommand : CommandBase
{
private readonly RubberduckParserState _state;
private readonly IIndenter _indenter;
private readonly INavigateCommand _navigateCommand;
public CodeExplorer_IndentCommand(RubberduckParserState state, IIndenter indenter, INavigateCommand navigateCommand)
{
_state = state;
_indenter = indenter;
_navigateCommand = navigateCommand;
}
public override bool CanExecute(object parameter)
{
if (parameter is CodeExplorerComponentViewModel)
{
var node = (CodeExplorerComponentViewModel)parameter;
if (node.Declaration.Annotations.Any(a => a.AnnotationType == AnnotationType.NoIndent))
{
return false;
}
}
if (parameter is CodeExplorerProjectViewModel)
{
if (_state.Status != ParserState.Ready)
{
return false;
}
var declaration = ((ICodeExplorerDeclarationViewModel)parameter).Declaration;
return _state.AllUserDeclarations
.Any(c => c.DeclarationType.HasFlag(DeclarationType.Module) &&
c.Annotations.All(a => a.AnnotationType != AnnotationType.NoIndent) &&
c.Project == declar
Solution
CodeExplorer_ImportCommand
If you know it, you can Assert it. Instead of an
CodeExplorer_PrintCommand
Your print command is creating a new Font instance once for each iteration of the loop. Creating it outside the loop would save on some memory and reduce garbage collection. I'm sure RD is beginning to be a memory hog. It'd be prudent to pay attention to such things.
CanExecute / Execute
You're doing all of the parameter validation in both of these methods. You could DRY this up by calling
Just had a thought. The command could be made thread safe by putting a lock on the
public override void Execute(object parameter)
{
// I know this will never be null because of the CanExecute
var project = ((ICodeExplorerDeclarationViewModel)parameter).Declaration.QualifiedName.QualifiedModuleName.Project;If you know it, you can Assert it. Instead of an
InvalidCastException, you get a very clear "The original developer thought this should never happen" message.CodeExplorer_PrintCommand
Your print command is creating a new Font instance once for each iteration of the loop. Creating it outside the loop would save on some memory and reduce garbage collection. I'm sure RD is beginning to be a memory hog. It'd be prudent to pay attention to such things.
CanExecute / Execute
You're doing all of the parameter validation in both of these methods. You could DRY this up by calling
CanExecute from inside of the Execute method, then doing direct casts instead of safe ones. I would also consider throwing an InvalidOperationException if Execute is called while it's not in a valid state to be called.public override void Execute(object parameter)
{
if ( !CanExecute(parameter) )
{
throw new InvalidOperationException("Be sure to check to see if we CanExecute before calling Execute!");
}
// now it's safe to use direct casts instead, at least in a single threaded environment.
}Just had a thought. The command could be made thread safe by putting a lock on the
parameter object, making it as good as your original implementation.public override void Execute(object parameter)
{
lock(parameter)
{
if ( !CanExecute(parameter) )
{
throw new InvalidOperationException("Be sure to check to see if we CanExecute before calling Execute!");
}
// now it's really safe to do the direct casts
} // exit lock
}Code Snippets
public override void Execute(object parameter)
{
// I know this will never be null because of the CanExecute
var project = ((ICodeExplorerDeclarationViewModel)parameter).Declaration.QualifiedName.QualifiedModuleName.Project;public override void Execute(object parameter)
{
if ( !CanExecute(parameter) )
{
throw new InvalidOperationException("Be sure to check to see if we CanExecute before calling Execute!");
}
// now it's safe to use direct casts instead, at least in a single threaded environment.
}public override void Execute(object parameter)
{
lock(parameter)
{
if ( !CanExecute(parameter) )
{
throw new InvalidOperationException("Be sure to check to see if we CanExecute before calling Execute!");
}
// now it's really safe to do the direct casts
} // exit lock
}Context
StackExchange Code Review Q#128375, answer score: 5
Revisions (0)
No revisions yet.