13
\$\begingroup\$

Next release of Rubberduck (should be 1.4) introduces a reorder parameters refactoring, which lets user reorder the parameters of a module (or class) member, and automatically adjust all usages.


Before:

DoSomething(ByVal a As String, ByVal b As Integer, ByVal c As Long)

After:

DoSomething(ByVal c As Long, ByVal b As Integer, ByVal a As String)

This is the IDialogView interface implementation:

interface IReorderParametersView : IDialogView
{
 Declaration Target { get; set; }
 List<Parameter> Parameters { get; set; }
 void InitializeParameterGrid();
}

And here is the Parameter class:

public class Parameter
{
 public string FullDeclaration { get; private set; }
 public int Index { get; private set; }
 public bool IsOptional { get; private set; }
 public bool IsParamArray { get; private set; }
 public Parameter(string fullDeclaration, int index)
 {
 FullDeclaration = fullDeclaration;
 Index = index;
 IsOptional = FullDeclaration.Contains("Optional");
 IsParamArray = FullDeclaration.Contains("ParamArray");
 }
}

This is the dialog code-behind:

public partial class ReorderParametersDialog : Form, IReorderParametersView
{
 public List<Parameter> Parameters { get; set; }
 private Parameter _selectedItem;
 private Rectangle _dragBoxFromMouseDown;
 Point _startPoint;
 private int _newRowIndex;
 public ReorderParametersDialog()
 {
 Parameters = new List<Parameter>();
 InitializeComponent();
 InitializeCaptions();
 MethodParametersGrid.SelectionChanged += MethodParametersGrid_SelectionChanged;
 MethodParametersGrid.MouseMove += MethodParametersGrid_MouseMove;
 MethodParametersGrid.MouseDown += MethodParametersGrid_MouseDown;
 MethodParametersGrid.DragOver += MethodParametersGrid_DragOver;
 MethodParametersGrid.DragDrop += MethodParametersGrid_DragDrop;
 }
 private void InitializeCaptions()
 {
 OkButton.Text = RubberduckUI.OkButtonText;
 CancelButton.Text = RubberduckUI.CancelButtonText;
 Text = RubberduckUI.ReorderParamsDialog_Caption;
 TitleLabel.Text = RubberduckUI.ReorderParamsDialog_TitleText;
 InstructionsLabel.Text = RubberduckUI.ReorderParamsDialog_InstructionsLabelText;
 MoveUpButton.Text = RubberduckUI.ReorderParamsDialog_MoveUpButtonText;
 MoveDownButton.Text = RubberduckUI.ReorderParamsDialog_MoveDownButtonText;
 }
 private void MethodParametersGrid_SelectionChanged(object sender, EventArgs e)
 {
 SelectionChanged();
 }
 private void MethodParametersGrid_MouseMove(object sender, MouseEventArgs e)
 {
 if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
 {
 if (_dragBoxFromMouseDown != Rectangle.Empty && !_dragBoxFromMouseDown.Contains(e.X, e.Y))
 {
 var dropEffect = MethodParametersGrid.DoDragDrop(
 MethodParametersGrid.Rows[_newRowIndex],
 DragDropEffects.Move);
 }
 }
 }
 private void MethodParametersGrid_MouseDown(object sender, MouseEventArgs e)
 {
 _newRowIndex = MethodParametersGrid.HitTest(e.X, e.Y).RowIndex;
 if (_newRowIndex == -1)
 {
 _dragBoxFromMouseDown = Rectangle.Empty;
 return;
 }
 _startPoint = new Point(e.X, e.Y);
 var dragSize = SystemInformation.DragSize;
 _dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2), e.Y - (dragSize.Height / 2)), dragSize);
 }
 private void MethodParametersGrid_DragOver(object sender, DragEventArgs e)
 {
 e.Effect = DragDropEffects.Move;
 }
 private void MethodParametersGrid_DragDrop(object sender, DragEventArgs e)
 {
 var clientPoint = MethodParametersGrid.PointToClient(new Point(e.X, e.Y));
 if (e.Effect == DragDropEffects.Move && _newRowIndex != -1)
 {
 var rowIndexOfItemUnderMouse = MethodParametersGrid.HitTest(clientPoint.X, clientPoint.Y).RowIndex;
 if (rowIndexOfItemUnderMouse < 0)
 {
 if (clientPoint.Y < _startPoint.Y)
 {
 rowIndexOfItemUnderMouse = 0;
 }
 else
 {
 rowIndexOfItemUnderMouse = Parameters.Count - 1;
 }
 }
 var tmp = Parameters.ElementAt(_newRowIndex);
 Parameters.RemoveAt(_newRowIndex);
 Parameters.Insert(rowIndexOfItemUnderMouse, tmp);
 ReselectParameter();
 }
 }
 public void InitializeParameterGrid()
 {
 MethodParametersGrid.AutoGenerateColumns = false;
 MethodParametersGrid.Columns.Clear();
 MethodParametersGrid.DataSource = Parameters;
 MethodParametersGrid.AlternatingRowsDefaultCellStyle.BackColor = Color.Lavender;
 MethodParametersGrid.MultiSelect = false;
 MethodParametersGrid.AllowUserToResizeRows = false;
 MethodParametersGrid.AllowDrop = true;
 MethodParametersGrid.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
 var column = new DataGridViewTextBoxColumn
 {
 Name = "Parameter",
 DataPropertyName = "FullDeclaration",
 HeaderText = "Parameter",
 ReadOnly = true,
 AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
 };
 MethodParametersGrid.Columns.Add(column);
 _selectedItem = Parameters[0];
 }
 private void OkButtonClick(object sender, EventArgs e)
 {
 OnOkButtonClicked();
 }
 public event EventHandler CancelButtonClicked;
 public void OnCancelButtonClicked()
 {
 Hide();
 }
 public event EventHandler OkButtonClicked;
 public void OnOkButtonClicked()
 {
 var handler = OkButtonClicked;
 if (handler != null)
 {
 handler(this, EventArgs.Empty);
 }
 }
 private Declaration _target;
 public Declaration Target
 {
 get { return _target; }
 set { _target = value; }
 }
 private void MoveUpButtonClicked(object sender, EventArgs e)
 {
 if (MethodParametersGrid.SelectedRows.Count == 0)
 {
 return;
 }
 var selectedIndex = MethodParametersGrid.SelectedRows[0].Index;
 SwapParameters(selectedIndex, selectedIndex - 1);
 ReselectParameter();
 }
 private void MoveDownButtonClicked(object sender, EventArgs e)
 {
 if (MethodParametersGrid.SelectedRows.Count == 0)
 {
 return;
 }
 var selectedIndex = MethodParametersGrid.SelectedRows[0].Index;
 SwapParameters(selectedIndex, selectedIndex + 1);
 ReselectParameter();
 }
 private void SwapParameters(int index1, int index2)
 {
 var tmp = Parameters[index1];
 Parameters[index1] = Parameters[index2];
 Parameters[index2] = tmp;
 }
 private void ReselectParameter()
 {
 MethodParametersGrid.Refresh();
 MethodParametersGrid.Rows
 .Cast<DataGridViewRow>()
 .Single(row => row.DataBoundItem == _selectedItem).Selected = true;
 SelectionChanged();
 }
 private void SelectionChanged()
 {
 _selectedItem = MethodParametersGrid.SelectedRows.Count == 0
 ? null
 : (Parameter)MethodParametersGrid.SelectedRows[0].DataBoundItem;
 MoveUpButton.Enabled = _selectedItem != null
 && MethodParametersGrid.SelectedRows[0].Index != 0;
 MoveDownButton.Enabled = _selectedItem != null
 && MethodParametersGrid.SelectedRows[0].Index != Parameters.Count - 1;
 }
}

And here is the presenter:

class ReorderParametersPresenter
{
 private readonly IReorderParametersView _view;
 private readonly Declarations _declarations;
 private readonly QualifiedSelection _selection;
 public ReorderParametersPresenter(IReorderParametersView view, VBProjectParseResult parseResult, QualifiedSelection selection)
 {
 _view = view;
 _declarations = parseResult.Declarations;
 _selection = selection;
 _view.OkButtonClicked += OnOkButtonClicked;
 }
 public void Show()
 {
 AcquireTarget(_selection);
 if (_view.Target != null)
 {
 LoadParameters();
 if (_view.Parameters.Count < 2) 
 {
 var message = string.Format(RubberduckUI.ReorderPresenter_LessThanTwoVariablesError, _view.Target.IdentifierName);
 MessageBox.Show(message, RubberduckUI.ReorderParamsDialog_TitleText, MessageBoxButtons.OK, MessageBoxIcon.Error);
 return; 
 }
 _view.InitializeParameterGrid();
 _view.ShowDialog();
 }
 }
 private void LoadParameters()
 {
 var procedure = (dynamic)_view.Target.Context;
 var argList = (VBAParser.ArgListContext)procedure.argList();
 var args = argList.arg();
 var index = 0;
 foreach (var arg in args)
 {
 _view.Parameters.Add(new Parameter(arg.GetText(), index++));
 }
 }
 private void OnOkButtonClicked(object sender, EventArgs e)
 {
 if (!_view.Parameters.Where((t, i) => t.Index != i).Any())
 {
 return;
 }
 var indexOfFirstOptionalParam = _view.Parameters.FindIndex(param => param.IsOptional);
 if (indexOfFirstOptionalParam >= 0)
 {
 for (var index = indexOfFirstOptionalParam + 1; index < _view.Parameters.Count; index++)
 {
 if (!_view.Parameters.ElementAt(index).IsOptional)
 {
 MessageBox.Show(RubberduckUI.ReorderPresenter_OptionalVariableError, RubberduckUI.ReorderParamsDialog_TitleText, MessageBoxButtons.OK, MessageBoxIcon.Error);
 return;
 }
 }
 }
 var indexOfParamArray = _view.Parameters.FindIndex(param => param.IsParamArray);
 if (indexOfParamArray >= 0)
 {
 if (indexOfParamArray != _view.Parameters.Count - 1)
 {
 MessageBox.Show(RubberduckUI.ReorderPresenter_ParamArrayError, RubberduckUI.ReorderParamsDialog_TitleText, MessageBoxButtons.OK, MessageBoxIcon.Error);
 return;
 }
 }
 AdjustSignatures();
 AdjustReferences(_view.Target.References);
 }
 private void AdjustReferences(IEnumerable<IdentifierReference> references)
 {
 foreach (var reference in references.Where(item => item.Context != _view.Target.Context))
 {
 var proc = (dynamic)reference.Context.Parent;
 var module = reference.QualifiedModuleName.Component.CodeModule;
 // This is to prevent throws when this statement fails:
 // (VBAParser.ArgsCallContext)proc.argsCall();
 try
 {
 var check = (VBAParser.ArgsCallContext)proc.argsCall();
 }
 catch
 {
 continue;
 }
 var argList = (VBAParser.ArgsCallContext)proc.argsCall();
 if (argList == null)
 {
 continue;
 }
 RewriteCall(reference, argList, module);
 }
 }
 private void RewriteCall(IdentifierReference reference, VBAParser.ArgsCallContext argList, Microsoft.Vbe.Interop.CodeModule module)
 {
 var paramNames = argList.argCall().Select(arg => arg.GetText()).ToList();
 var lineCount = argList.Stop.Line - argList.Start.Line + 1; // adjust for total line count
 var variableIndex = 0;
 for (var line = argList.Start.Line; line < argList.Start.Line + lineCount; line++)
 {
 var newContent = module.Lines[line, 1].Replace(" , ", "");
 var currentStringIndex = line == argList.Start.Line ? reference.Declaration.IdentifierName.Length : 0;
 for (var i = 0; i < paramNames.Count && variableIndex < _view.Parameters.Count; i++)
 {
 var variableStringIndex = newContent.IndexOf(paramNames.ElementAt(i), currentStringIndex);
 if (variableStringIndex > -1)
 {
 if (_view.Parameters.ElementAt(variableIndex).Index >= paramNames.Count)
 {
 newContent = newContent.Insert(variableStringIndex, " , ");
 i--;
 variableIndex++;
 continue;
 }
 var oldVariableString = paramNames.ElementAt(i);
 var newVariableString = paramNames.ElementAt(_view.Parameters.ElementAt(variableIndex).Index);
 var beginningSub = newContent.Substring(0, variableStringIndex);
 var replaceSub = newContent.Substring(variableStringIndex).Replace(oldVariableString, newVariableString);
 newContent = beginningSub + replaceSub;
 variableIndex++;
 currentStringIndex = beginningSub.Length + newVariableString.Length;
 }
 }
 module.ReplaceLine(line, newContent);
 }
 }
 private void AdjustSignatures()
 {
 var proc = (dynamic)_view.Target.Context;
 var argList = (VBAParser.ArgListContext)proc.argList();
 var module = _view.Target.QualifiedName.QualifiedModuleName.Component.CodeModule;
 // if we are reordering a property getter, check if we need to reorder a letter/setter too
 if (_view.Target.DeclarationType == DeclarationType.PropertyGet)
 {
 var setter = _declarations.Items.FirstOrDefault(item => item.ParentScope == _view.Target.ParentScope &&
 item.IdentifierName == _view.Target.IdentifierName &&
 item.DeclarationType == DeclarationType.PropertySet);
 if (setter != null)
 {
 AdjustSignatures(setter);
 }
 var letter = _declarations.Items.FirstOrDefault(item => item.ParentScope == _view.Target.ParentScope &&
 item.IdentifierName == _view.Target.IdentifierName &&
 item.DeclarationType == DeclarationType.PropertyLet);
 if (letter != null)
 {
 AdjustSignatures(letter);
 }
 }
 RewriteSignature(argList, module);
 foreach (var withEvents in _declarations.Items.Where(item => item.IsWithEvents && item.AsTypeName == _view.Target.ComponentName))
 {
 foreach (var reference in _declarations.FindEventProcedures(withEvents))
 {
 AdjustSignatures(reference);
 }
 }
 var interfaceImplementations = _declarations.FindInterfaceImplementationMembers()
 .Where(item => item.Project.Equals(_view.Target.Project) &&
 item.IdentifierName == _view.Target.ComponentName + "_" + _view.Target.IdentifierName);
 foreach (var interfaceImplentation in interfaceImplementations)
 {
 AdjustSignatures(interfaceImplentation);
 AdjustReferences(interfaceImplentation.References);
 }
 }
 private void AdjustSignatures(IdentifierReference reference)
 {
 var proc = (dynamic)reference.Context.Parent;
 var module = reference.QualifiedModuleName.Component.CodeModule;
 var argList = (VBAParser.ArgListContext)proc.argList();
 RewriteSignature(argList, module);
 }
 private void AdjustSignatures(Declaration reference)
 {
 var proc = (dynamic)reference.Context.Parent;
 var module = reference.QualifiedName.QualifiedModuleName.Component.CodeModule;
 VBAParser.ArgListContext argList;
 if (reference.DeclarationType == DeclarationType.PropertySet || reference.DeclarationType == DeclarationType.PropertyLet)
 {
 argList = (VBAParser.ArgListContext)proc.children[0].argList();
 }
 else
 {
 argList = (VBAParser.ArgListContext)proc.subStmt().argList();
 }
 RewriteSignature(argList, module);
 }
 private void RewriteSignature(VBAParser.ArgListContext argList, Microsoft.Vbe.Interop.CodeModule module)
 {
 var args = argList.arg();
 var variableIndex = 0;
 for (var lineNum = argList.Start.Line; lineNum < argList.Start.Line + argList.GetSelection().LineCount; lineNum++)
 {
 var newContent = module.Lines[lineNum, 1];
 var currentStringIndex = 0;
 for (var i = variableIndex; i < _view.Parameters.Count; i++)
 {
 var variableStringIndex = newContent.IndexOf(_view.Parameters.Find(item => item.Index == variableIndex).FullDeclaration, currentStringIndex);
 if (variableStringIndex > -1)
 {
 var oldVariableString = _view.Parameters.Find(item => item.Index == variableIndex).FullDeclaration;
 var newVariableString = _view.Parameters.ElementAt(i).FullDeclaration;
 var beginningSub = newContent.Substring(0, variableStringIndex);
 var replaceSub = newContent.Substring(variableStringIndex).Replace(oldVariableString, newVariableString);
 newContent = beginningSub + replaceSub;
 variableIndex++;
 currentStringIndex = beginningSub.Length + newVariableString.Length;
 }
 }
 module.ReplaceLine(lineNum, newContent);
 }
 }
 private static readonly DeclarationType[] ValidDeclarationTypes =
 {
 DeclarationType.Event,
 DeclarationType.Function,
 DeclarationType.Procedure,
 DeclarationType.PropertyGet,
 DeclarationType.PropertyLet,
 DeclarationType.PropertySet
 };
 private void AcquireTarget(QualifiedSelection selection)
 {
 var target = _declarations.Items
 .Where(item => !item.IsBuiltIn)
 .FirstOrDefault(item => IsSelectedDeclaration(selection, item)
 || IsSelectedReference(selection, item));
 if (target == null || !ValidDeclarationTypes.Contains(target.DeclarationType))
 {
 FindTarget(ref target, selection);
 }
 if (target != null && target.DeclarationType == DeclarationType.PropertySet)
 {
 var getter = _declarations.Items.FirstOrDefault(item => item.ParentScope == target.ParentScope &&
 item.IdentifierName == target.IdentifierName &&
 item.DeclarationType == DeclarationType.PropertyGet);
 if (getter != null)
 {
 target = getter;
 }
 }
 PromptIfTargetImplementsInterface(ref target);
 _view.Target = target;
 }
 private void FindTarget(ref Declaration target, QualifiedSelection selection)
 {
 var targets = _declarations.Items
 .Where(item => !item.IsBuiltIn
 && item.ComponentName == selection.QualifiedName.ComponentName
 && ValidDeclarationTypes.Contains(item.DeclarationType));
 foreach (var declaration in targets)
 {
 var startLine = declaration.Context.GetSelection().StartLine;
 var startColumn = declaration.Context.GetSelection().StartColumn;
 var endLine = declaration.Context.GetSelection().EndLine;
 var endColumn = declaration.Context.GetSelection().EndColumn;
 if (startLine <= selection.Selection.StartLine && endLine >= selection.Selection.EndLine)
 {
 if (startLine == selection.Selection.StartLine && startColumn > selection.Selection.StartColumn)
 {
 continue;
 }
 if (endLine == selection.Selection.EndLine && endColumn < selection.Selection.EndColumn)
 {
 continue;
 }
 target = declaration;
 }
 foreach (var reference in declaration.References)
 {
 var proc = (dynamic)reference.Context.Parent;
 // This is to prevent throws when this statement fails:
 // (VBAParser.ArgsCallContext)proc.argsCall();
 try
 {
 var check = (VBAParser.ArgsCallContext)proc.argsCall();
 }
 catch
 {
 continue;
 }
 var argList = (VBAParser.ArgsCallContext)proc.argsCall();
 if (argList == null)
 {
 continue;
 }
 startLine = argList.Start.Line;
 startColumn = argList.Start.Column;
 endLine = argList.Stop.Line;
 endColumn = argList.Stop.Column + argList.Stop.Text.Length + 1;
 if ((startLine <= selection.Selection.StartLine && endLine >= selection.Selection.EndLine) && 
 (startLine == selection.Selection.StartLine && startColumn > selection.Selection.StartColumn ||
 endLine == selection.Selection.EndLine && endColumn < selection.Selection.EndColumn))
 {
 continue;
 }
 target = reference.Declaration;
 return;
 }
 }
 }
 private void PromptIfTargetImplementsInterface(ref Declaration target)
 {
 var declaration = target;
 var interfaceImplementation = _declarations.FindInterfaceImplementationMembers().SingleOrDefault(m => m.Equals(declaration));
 if (target == null || interfaceImplementation == null)
 {
 return;
 }
 var interfaceMember = _declarations.FindInterfaceMember(interfaceImplementation);
 var message = string.Format(RubberduckUI.ReorderPresenter_TargetIsInterfaceMemberImplementation, target.IdentifierName, interfaceMember.ComponentName, interfaceMember.IdentifierName);
 var confirm = MessageBox.Show(message, RubberduckUI.ReorderParamsDialog_TitleText, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
 if (confirm == DialogResult.No)
 {
 target = null;
 return;
 }
 target = interfaceMember;
 }
 private bool IsSelectedReference(QualifiedSelection selection, Declaration declaration)
 {
 return declaration.References.Any(r =>
 r.QualifiedModuleName == selection.QualifiedName &&
 r.Selection.ContainsFirstCharacter(selection.Selection));
 }
 private bool IsSelectedDeclaration(QualifiedSelection selection, Declaration declaration)
 {
 return declaration.QualifiedName.QualifiedModuleName == selection.QualifiedName
 && (declaration.Selection.ContainsFirstCharacter(selection.Selection));
 }
}

My main concerns are how similar certain sections of the code are, any ideas to make this less repetitive are welcome.

Mathieu Guindon
75.5k18 gold badges194 silver badges467 bronze badges
asked May 20, 2015 at 1:25
\$\endgroup\$

3 Answers 3

4
\$\begingroup\$

In the Show() method, this condition could be reversed into a positive one:

if (_view.Target != null)
{
 ...
}

...which would reduce nesting because if the _view.Target is null, then there's nothing to act upon and the method can return early:

if (_view.Target == null)
{
 return;
}

The name of the resource string RubberduckUI.ReorderPresenter_LessThanTwoVariablesError is slightly off - a better name would be ReorderPresenter_LessThanTwoParametersError, or perhaps ReorderPresenter_RequiresTwoOrMoreParametersError.

There's a similar scheme with ReorderPresenter_OptionalVariableError: a "variable" isn't a "parameter", and the resource string could have a name that better explains why this is happening - like ReorderPresenter_OptionalParametersMustBeLastError.

I don't like that you're referring to parameters as "variables" in multiple places; there's a DeclarationType.Variable enum member, and then there's a DeclarationType.Parameter enum member - they are two completely distinct things.

From a UX standpoint, there's an opportunity here: a Parameter knows when it's Optional, so you have a way to validate the data before the user hits the Ok button. One way to implement this could be to remove the column heading, and insert a column before the "Parameters" column, that would display some [x] icon (Rubberduck uses such an icon elsewhere for similar purposes) with a tooltip explaining what's wrong; the Ok button could be disabled when an icon is shown in that column.

This would greatly simplify the OnOkButtonClicked handler, which has a confusing name (I've been guilty of that too) - OnXxxx should be for procedures that raise an event, not handle it.

Why is AdjustSignatures plural? It's only ever processing one signature at a time, right?


A Declaration called reference is confusing:

private void AdjustSignatures(Declaration reference)

Either you have a Declaration, or you have one of its references!

answered May 20, 2015 at 14:29
\$\endgroup\$
4
\$\begingroup\$

Looks fairly clean to me for the most part.

You could reduce nesting a bit here by extracting a boolean method in the code behind.

private void MethodParametersGrid_MouseMove(object sender, MouseEventArgs e)
{
 if (ShouldDragDrop(e, _dragBoxFromMouseDown))
 {
 var dropEffect = MethodParametersGrid.DoDragDrop(
 MethodParametersGrid.Rows[_newRowIndex],
 DragDropEffects.Move);
 }
}

This also looks like a good opportunity to extract a method for clarity's sake.

_dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2), e.Y - (dragSize.Height / 2)), dragSize);

Remember, removing duplication isn't the only reason to create a method. It's also used to provide useful abstractions.

In MoveUpButtonClicked and MoveDownButtonClicked this line of code is duplicated.

var selectedIndex = MethodParametersGrid.SelectedRows[0].Index;

It might be worth extracting another method for it.

answered May 20, 2015 at 14:38
\$\endgroup\$
1
\$\begingroup\$

The FindTarget() method, which I believe has some bugs in this version, is very messy:

private void FindTarget(ref Declaration target, QualifiedSelection selection)
{
 var targets = _declarations.Items
 .Where(item => !item.IsBuiltIn
 && item.ComponentName == selection.QualifiedName.ComponentName
 && ValidDeclarationTypes.Contains(item.DeclarationType));
 foreach (var declaration in targets)
 {
 var startLine = declaration.Context.GetSelection().StartLine;
 var startColumn = declaration.Context.GetSelection().StartColumn;
 var endLine = declaration.Context.GetSelection().EndLine;
 var endColumn = declaration.Context.GetSelection().EndColumn;
 if (startLine <= selection.Selection.StartLine && endLine >= selection.Selection.EndLine)
 {
 if (startLine == selection.Selection.StartLine && startColumn > selection.Selection.StartColumn)
 {
 continue;
 }
 if (endLine == selection.Selection.EndLine && endColumn < selection.Selection.EndColumn)
 {
 continue;
 }
 target = declaration;
 }
 foreach (var reference in declaration.References)
 {
 var proc = (dynamic)reference.Context.Parent;
 // This is to prevent throws when this statement fails:
 // (VBAParser.ArgsCallContext)proc.argsCall();
 try
 {
 var check = (VBAParser.ArgsCallContext)proc.argsCall();
 }
 catch
 {
 continue;
 }
 var argList = (VBAParser.ArgsCallContext)proc.argsCall();
 if (argList == null)
 {
 continue;
 }
 startLine = argList.Start.Line;
 startColumn = argList.Start.Column;
 endLine = argList.Stop.Line;
 endColumn = argList.Stop.Column + argList.Stop.Text.Length + 1;
 if ((startLine <= selection.Selection.StartLine && endLine >= selection.Selection.EndLine) && 
 (startLine == selection.Selection.StartLine && startColumn > selection.Selection.StartColumn ||
 endLine == selection.Selection.EndLine && endColumn < selection.Selection.EndColumn))
 {
 continue;
 }
 target = reference.Declaration;
 return;
 }
 }
}

I can clean this up by using the defined type Selection():

private Declaration FindTarget(QualifiedSelection selection, DeclarationType[] validDeclarationTypes)
{
 var target = _declarations.Items
 .Where(item => !item.IsBuiltIn)
 .FirstOrDefault(item => IsSelectedDeclaration(selection, item)
 || IsSelectedReference(selection, item));
 if (target != null && validDeclarationTypes.Contains(target.DeclarationType))
 {
 return target;
 }
 target = null;
 var targets = _declarations.Items
 .Where(item => !item.IsBuiltIn
 && item.ComponentName == selection.QualifiedName.ComponentName
 && validDeclarationTypes.Contains(item.DeclarationType));
 var currentSelection = new Selection(0, 0, int.MaxValue, int.MaxValue);
 foreach (var declaration in targets)
 {
 var declarationSelection = new Selection(declaration.Context.Start.Line,
 declaration.Context.Start.Column,
 declaration.Context.Stop.Line,
 declaration.Context.Stop.Column);
 if (currentSelection.Contains(declarationSelection) && declarationSelection.Contains(selection.Selection))
 {
 target = declaration;
 currentSelection = declarationSelection;
 }
 foreach (var reference in declaration.References)
 {
 var proc = (dynamic)reference.Context.Parent;
 VBAParser.ArgsCallContext paramList;
 // This is to prevent throws when this statement fails:
 // (VBAParser.ArgsCallContext)proc.argsCall();
 try
 {
 paramList = (VBAParser.ArgsCallContext)proc.argsCall();
 }
 catch
 {
 continue;
 }
 if (paramList == null) { continue; }
 var referenceSelection = new Selection(paramList.Start.Line,
 paramList.Start.Column,
 paramList.Stop.Line,
 paramList.Stop.Column + paramList.Stop.Text.Length + 1);
 if (currentSelection.Contains(declarationSelection) && referenceSelection.Contains(selection.Selection))
 {
 target = reference.Declaration;
 currentSelection = referenceSelection;
 }
 }
 }
 return target;
}

In addition, I have made it much more generic, and it returns a value instead of modifying one.

answered Jun 3, 2015 at 21:54
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.