patterncsharpMinor
A status bar for the VBE
Viewed 0 times
barthestatusvbefor
Problem
One of the most annoying things about the VBE (VBA's IDE), is that it doesn't have a status bar.
Rubberduck 2.0 works around this by introducing the
Because Rubberduck loads all referenced COM types and resolves references regardless of whether or not an identifier is built-in, the "status bar" also displays type information when the selection is on an identifier reference for a built-in declaration, e.g. an
...or even built-in functions from the VBA standard library:
If that's a wonderful feature, the code for it is a little less "wonderful". Here's the entire
```
public class RubberduckCommandBar
{
private readonly RubberduckParserState _state;
private readonly VBE _vbe;
private readonly IShowParserErrorsCommand _command;
private CommandBarButton _refreshButton;
private CommandBarButton _statusButton;
private CommandBarButton _selectionButton;
public RubberduckCommandBar(RubberduckParserState state, VBE vbe, IShowParserErrorsCommand command)
{
_state = state;
_vbe = vbe;
_command = command;
_state.StateChanged += State_StateChanged;
Initialize();
}
private void _statusButton_Click(CommandBarButton Ctrl, ref bool CancelDefault)
{
if (_state.Status == ParserState.Error)
{
_command.Execute(null);
}
}
public void SetStatusText(string value = null)
{
UiDispatcher.Invoke(() => _statusButton.Caption = value ?? RubberduckUI.ResourceManager.GetString("ParserState_" + _state.Status));
}
public void SetSelectionText(Declaration declar
Rubberduck 2.0 works around this by introducing the
RubberduckCommandBar, which [ab]uses msoControlButton controls as though they were labels - the command bar constantly resizes as the text changes, but at least we get to show context-sensitive information about the current selection:Because Rubberduck loads all referenced COM types and resolves references regardless of whether or not an identifier is built-in, the "status bar" also displays type information when the selection is on an identifier reference for a built-in declaration, e.g. an
Excel.ListObject:...or even built-in functions from the VBA standard library:
If that's a wonderful feature, the code for it is a little less "wonderful". Here's the entire
RubberduckCommandBar class, I'm particularly unimpressed with the SetSelectionText implementation - how can it be written better?```
public class RubberduckCommandBar
{
private readonly RubberduckParserState _state;
private readonly VBE _vbe;
private readonly IShowParserErrorsCommand _command;
private CommandBarButton _refreshButton;
private CommandBarButton _statusButton;
private CommandBarButton _selectionButton;
public RubberduckCommandBar(RubberduckParserState state, VBE vbe, IShowParserErrorsCommand command)
{
_state = state;
_vbe = vbe;
_command = command;
_state.StateChanged += State_StateChanged;
Initialize();
}
private void _statusButton_Click(CommandBarButton Ctrl, ref bool CancelDefault)
{
if (_state.Status == ParserState.Error)
{
_command.Execute(null);
}
}
public void SetStatusText(string value = null)
{
UiDispatcher.Invoke(() => _statusButton.Caption = value ?? RubberduckUI.ResourceManager.GetString("ParserState_" + _state.Status));
}
public void SetSelectionText(Declaration declar
Solution
By adding a small nesting inside the first
you could extract the condition check of the
The
but if I read
if statement you can skip the multiple checks for declaration != null which would make the code a little bit more readable like so public void SetSelectionText(Declaration declaration)
{
if (declaration == null)
{
if(_vbe.ActiveCodePane == null)
{
return;
}
var selection = _vbe.ActiveCodePane.GetSelection();
SetSelectionText(selection);
}
else if (!declaration.IsBuiltIn && declaration.DeclarationType != DeclarationType.Class && declaration.DeclarationType != DeclarationType.Module)
{
_selectionButton.Caption = string.Format("{0} ({1}): {2} ({3})",
declaration.QualifiedName.QualifiedModuleName,
declaration.QualifiedSelection.Selection,
declaration.IdentifierName,
RubberduckUI.ResourceManager.GetString("DeclarationType_" + declaration.DeclarationType));
}
else
{
var selection = _vbe.ActiveCodePane.GetSelection();
_selectionButton.Caption = string.Format("{0}: {1} ({2}) {3}",
declaration.QualifiedName.QualifiedModuleName,
declaration.IdentifierName,
RubberduckUI.ResourceManager.GetString("DeclarationType_" + declaration.DeclarationType),
selection.Selection);
}
}you could extract the condition check of the
else if to a method with a meaningful name. Although it would be only used once, it could improve the readability of the current method. The
FindSelectedDeclaration() method of the RubberduckParserState class could use some facelifting as well. If you swith the condition here if (!selection.Equals(default(QualifiedSelection))) you can return early and would save one level of indentation like so public Declaration FindSelectedDeclaration(CodePane activeCodePane)
{
var selection = activeCodePane.GetSelection();
if (selection.Equals(_lastSelection))
{
return _selectedDeclaration;
}
_lastSelection = selection;
_selectedDeclaration = null;
if (selection.Equals(default(QualifiedSelection)))
{
return null;
}
var matches = AllDeclarations
.Where(item => item.DeclarationType != DeclarationType.Project &&
item.DeclarationType != DeclarationType.ModuleOption &&
item.DeclarationType != DeclarationType.Class &&
item.DeclarationType != DeclarationType.Module &&
(IsSelectedDeclaration(selection, item) ||
item.References.Any(reference => IsSelectedReference(selection, reference))));
try
{
var match = matches.SingleOrDefault() ?? AllUserDeclarations
.SingleOrDefault(item => (item.DeclarationType == DeclarationType.Class || item.DeclarationType == DeclarationType.Module)
&& item.QualifiedName.QualifiedModuleName.Equals(selection.QualifiedName));
_selectedDeclaration = match;
}
catch (InvalidOperationException exception)
{
Debug.WriteLine(exception);
}
if (_selectedDeclaration != null)
{
Debug.WriteLine("Current selection ({0}) is '{1}' ({2})", selection, _selectedDeclaration.IdentifierName, _selectedDeclaration.DeclarationType);
}
return _selectedDeclaration;
}but if I read
var matches or var match I always think about regular expressions, so I would prefer to rename these variables to foundDeclarations and foundDeclaration.Code Snippets
public void SetSelectionText(Declaration declaration)
{
if (declaration == null)
{
if(_vbe.ActiveCodePane == null)
{
return;
}
var selection = _vbe.ActiveCodePane.GetSelection();
SetSelectionText(selection);
}
else if (!declaration.IsBuiltIn && declaration.DeclarationType != DeclarationType.Class && declaration.DeclarationType != DeclarationType.Module)
{
_selectionButton.Caption = string.Format("{0} ({1}): {2} ({3})",
declaration.QualifiedName.QualifiedModuleName,
declaration.QualifiedSelection.Selection,
declaration.IdentifierName,
RubberduckUI.ResourceManager.GetString("DeclarationType_" + declaration.DeclarationType));
}
else
{
var selection = _vbe.ActiveCodePane.GetSelection();
_selectionButton.Caption = string.Format("{0}: {1} ({2}) {3}",
declaration.QualifiedName.QualifiedModuleName,
declaration.IdentifierName,
RubberduckUI.ResourceManager.GetString("DeclarationType_" + declaration.DeclarationType),
selection.Selection);
}
}public Declaration FindSelectedDeclaration(CodePane activeCodePane)
{
var selection = activeCodePane.GetSelection();
if (selection.Equals(_lastSelection))
{
return _selectedDeclaration;
}
_lastSelection = selection;
_selectedDeclaration = null;
if (selection.Equals(default(QualifiedSelection)))
{
return null;
}
var matches = AllDeclarations
.Where(item => item.DeclarationType != DeclarationType.Project &&
item.DeclarationType != DeclarationType.ModuleOption &&
item.DeclarationType != DeclarationType.Class &&
item.DeclarationType != DeclarationType.Module &&
(IsSelectedDeclaration(selection, item) ||
item.References.Any(reference => IsSelectedReference(selection, reference))));
try
{
var match = matches.SingleOrDefault() ?? AllUserDeclarations
.SingleOrDefault(item => (item.DeclarationType == DeclarationType.Class || item.DeclarationType == DeclarationType.Module)
&& item.QualifiedName.QualifiedModuleName.Equals(selection.QualifiedName));
_selectedDeclaration = match;
}
catch (InvalidOperationException exception)
{
Debug.WriteLine(exception);
}
if (_selectedDeclaration != null)
{
Debug.WriteLine("Current selection ({0}) is '{1}' ({2})", selection, _selectedDeclaration.IdentifierName, _selectedDeclaration.DeclarationType);
}
return _selectedDeclaration;
}Context
StackExchange Code Review Q#125685, answer score: 3
Revisions (0)
No revisions yet.