Skip to content
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
79 changes: 66 additions & 13 deletions RetailCoder.VBE/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
using System.Windows.Forms;
using Rubberduck.UI.Command;
using Rubberduck.UI.Command.MenuItems.CommandBars;
using Rubberduck.VBEditor.SafeComWrappers;
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
using Rubberduck.VBEditor.SafeComWrappers.MSForms;
using Rubberduck.VBEditor.SafeComWrappers.Office.Core.Abstract;
using Rubberduck.VersionCheck;

namespace Rubberduck
Expand Down Expand Up @@ -88,26 +91,76 @@ private void _hooks_MessageReceived(object sender, HookEventArgs e)

private void RefreshSelection()
{

var caption = String.Empty;
var refCount = 0;

WindowKind windowKind = _vbe.ActiveWindow.Type;
var pane = _vbe.ActiveCodePane;
var component = _vbe.SelectedVBComponent;

Declaration selectedDeclaration = null;

//TODO - I doubt this is the best way to check if the SelectedVBComponent and the ActiveCodePane are the same component.
if (windowKind == WindowKind.CodeWindow || (!_vbe.SelectedVBComponent.IsWrappingNullReference
&& component.ParentProject.ProjectId == pane.CodeModule.Parent.ParentProject.ProjectId
&& component.Name == pane.CodeModule.Parent.Name))
{
Declaration selectedDeclaration = null;
if (!pane.IsWrappingNullReference)
selectedDeclaration = _parser.State.FindSelectedDeclaration(pane);
refCount = selectedDeclaration == null ? 0 : selectedDeclaration.References.Count();
caption = _stateBar.GetContextSelectionCaption(_vbe.ActiveCodePane, selectedDeclaration);
}
else if (windowKind == WindowKind.Designer)
{
caption = GetComponentControlsCaption(component);
}
else
{
if (_vbe.SelectedVBComponent.IsWrappingNullReference)
{
selectedDeclaration = _parser.State.FindSelectedDeclaration(pane);
var refCount = selectedDeclaration == null ? 0 : selectedDeclaration.References.Count();
var caption = _stateBar.GetContextSelectionCaption(_vbe.ActiveCodePane, selectedDeclaration);
_stateBar.SetContextSelectionCaption(caption, refCount);
//The user might have selected the project node in Project Explorer
//If they've chosen a folder, we'll return the project anyway
caption = !_vbe.ActiveVBProject.IsWrappingNullReference
? _vbe.ActiveVBProject.Name
: null;
}

var currentStatus = _parser.State.Status;
if (ShouldEvaluateCanExecute(selectedDeclaration, currentStatus))
else
{
_appMenus.EvaluateCanExecute(_parser.State);
_stateBar.EvaluateCanExecute(_parser.State);
caption = component.Type == ComponentType.UserForm && component.HasOpenDesigner
? GetComponentControlsCaption(component)
: String.Format("{0}.{1} ({2})", component.ParentProject.Name, component.Name, component.Type);
}
}

_stateBar.SetContextSelectionCaption(caption, refCount);

var currentStatus = _parser.State.Status;
if (ShouldEvaluateCanExecute(selectedDeclaration, currentStatus))
{
_appMenus.EvaluateCanExecute(_parser.State);
_stateBar.EvaluateCanExecute(_parser.State);
}

_lastStatus = currentStatus;
_lastSelectedDeclaration = selectedDeclaration;
_lastStatus = currentStatus;
_lastSelectedDeclaration = selectedDeclaration;
}

private string GetComponentControlsCaption(IVBComponent component)
{
switch (component.SelectedControls.Count)
{
case 0:
//TODO get the real designer for VB6
return String.Format("{0}.{1} ({2})", component.ParentProject.Name, component.Name, "MSForms.UserForm");
break;
case 1:
//TODO return the libraryName.className of the control
IControl control = component.SelectedControls.First();
return String.Format("{0}.{1}.{2} ({3})", component.ParentProject.Name, component.Name, control.Name, control.TypeName());
break;
default:
return String.Format("{0}.{1} ({2})", component.ParentProject.Name, component.Name, RubberduckUI.ContextMultipleControlsSelection);
break;
}
}

Expand Down
2 changes: 2 additions & 0 deletions RetailCoder.VBE/Common/RubberduckHooks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ private IntPtr WindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam)
}
break;
case WM.CLOSE:
case WM.RUBBERDUCK_SINKING:
suppress = true;
Detach();
break;
}
Expand Down
190 changes: 102 additions & 88 deletions RetailCoder.VBE/Inspections/QuickFixes/AssignedByValParameterQuickFix.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Rubberduck.Inspections.Abstract;
using System;
using System.Collections.Generic;
using System.Linq;
using Rubberduck.Parsing;
using Rubberduck.VBEditor;
Expand All @@ -10,118 +9,137 @@
using System.Windows.Forms;
using Rubberduck.UI.Refactorings;
using Rubberduck.Common;
using System.Text.RegularExpressions;
using System.Collections.Generic;

namespace Rubberduck.Inspections.QuickFixes
{
public class AssignedByValParameterQuickFix : QuickFixBase
{
private readonly Declaration _target;
private string _localCopyVariableName;
private bool _forceUseOfSuggestedName;
private readonly string[] _originalCodeLines;
private bool _isQuickFixUnitTest;
private string[] _originalProcCodeLines;

public AssignedByValParameterQuickFix(Declaration target, QualifiedSelection selection)
: base(target.Context, selection, InspectionsUI.AssignedByValParameterQuickFix)
{
_target = target;
_forceUseOfSuggestedName = false;
_localCopyVariableName = string.Empty;

_originalCodeLines = GetMethodLines();
_isQuickFixUnitTest = false;
_localCopyVariableName = SuggestedName();
_originalProcCodeLines = GetProcedureLines();
}

public override bool CanFixInModule { get { return false; } }
public override bool CanFixInProject { get { return false; } }

//This function exists solely to support unit testing - by preventing the popup dialog
//This function exists solely to support unit testing
public void TESTONLY_FixUsingAutoGeneratedName()
{
_forceUseOfSuggestedName = true;
//Prevent the popup dialog and forces the use of the AutoSuggestedName
_isQuickFixUnitTest = true;

Fix();
}

public override void Fix()
{
if (_forceUseOfSuggestedName)
{
_localCopyVariableName = AutoSuggestedName();
IsCancelled = false;
}
else
{
GetLocalCopyVariableNameFromUser();
}

if (!IsCancelled)
{
ModifyBlockToUseLocalCopyVariable();
}
SetLocalCopyVariableName();

if (IsCancelled) { return; }

ModifyBlockToUseLocalCopyVariable();
}

private void GetLocalCopyVariableNameFromUser()
private void SetLocalCopyVariableName()
{
using (var view = new AssignedByValParameterQuickFixDialog(_originalCodeLines))
using (var view = new AssignedByValParameterQuickFixDialog(_originalProcCodeLines))
{
view.Target = _target;
view.NewName = AutoSuggestedName();
view.ShowDialog();

IsCancelled = view.DialogResult == DialogResult.Cancel;
if (!IsCancelled) { _localCopyVariableName = view.NewName; }
view.NewName = SuggestedName();
if (!_isQuickFixUnitTest)
{
view.ShowDialog();
IsCancelled = view.DialogResult == DialogResult.Cancel;
}
if (!IsCancelled)
{
_localCopyVariableName = view.NewName;
}
}
}

private void ModifyBlockToUseLocalCopyVariable()
{
if(ProposedNameIsInUse()) { return; }
if(!CheckLocalVariableNameIsValidForUpdate())
{
return;
}

var module = Selection.QualifiedName.Component.CodeModule;
var startLine = Selection.Selection.StartLine;
ReplaceAssignedByValParameterReferences();

module.InsertLines(++startLine, BuildLocalCopyDeclaration());
module.InsertLines(++startLine, BuildLocalCopyAssignment());
var moduleLines = GetModuleLines();
//moduleLines array index is zero-based
var endOfScopeStatement = GetEndOfScopeStatementForDeclaration(moduleLines[Selection.Selection.StartLine - 1]);

var isInScope = true;
int zbIndex; //Zero-Based index for moduleLines array
//starts with lines after the above inserts
for (zbIndex = startLine ; isInScope && zbIndex < module.CountOfLines; zbIndex++)
AddDeclarationAndAssignment();
}
private bool CheckLocalVariableNameIsValidForUpdate()
{
if (_localCopyVariableName.Equals(string.Empty))
{
var obIndex = zbIndex + 1; //One-Based index for module object
if (LineRequiresUpdate(moduleLines[zbIndex]))
{
var newStatement = moduleLines[zbIndex].Replace(_target.IdentifierName, _localCopyVariableName);
module.ReplaceLine(obIndex, newStatement);
}
isInScope = !moduleLines[zbIndex].Contains(endOfScopeStatement);
return false;
}
var validator = new VariableNameValidator(_localCopyVariableName);
if (_originalProcCodeLines.Any(c => validator.IsFoundIn(c)))
{
return false;
}
return validator.IsValidName();
}

private bool ProposedNameIsInUse()
private void ReplaceAssignedByValParameterReferences()
{
return GetMethodLines().Any(c => c.Contains(Tokens.Dim + " " + _localCopyVariableName + " "));
var moduleLines = GetAllModuleLines();
foreach (IdentifierReference idRef in _target.References)
{
var zbIndex = idRef.Selection.StartLine - 1; //moduleLines is zero-based index
var newStatement = ReplaceByValReferencesInLine(moduleLines[zbIndex]);
var module = Selection.QualifiedName.Component.CodeModule;
module.ReplaceLine(idRef.Selection.StartLine, newStatement);
}
}

private bool LineRequiresUpdate(string line)
private void AddDeclarationAndAssignment()
{
return line.Contains(" " + _target.IdentifierName + " ")
|| line.Contains(NameAsLeftHandSide())
|| line.Contains(NameAsRightHandSide())
|| line.Contains(NameAsObjectMethodOrAccessorCall())
|| line.Contains(NameAsSubOrFunctionParam())
|| line.Contains(NameAsSubOrFunctionParamFirst())
|| line.Contains(NameAsSubOrFunctionParamLast());
var startLine = Selection.Selection.StartLine;
var module = Selection.QualifiedName.Component.CodeModule;
module.InsertLines(++startLine, BuildLocalCopyDeclaration());
module.InsertLines(++startLine, BuildLocalCopyAssignment());
}

private string NameAsLeftHandSide() { return _target.IdentifierName + " "; }
private string NameAsRightHandSide() { return " " + _target.IdentifierName; }
private string NameAsObjectMethodOrAccessorCall() { return " " + _target.IdentifierName + "."; }
private string NameAsSubOrFunctionParam() { return _target.IdentifierName + ","; }
private string NameAsSubOrFunctionParamFirst() { return "(" + _target.IdentifierName; }
private string NameAsSubOrFunctionParamLast() { return _target.IdentifierName + ")"; }

private string ReplaceByValReferencesInLine(string input)
{
const string noAdjacentLettersNumbersOrUnderscores = "([^0-9a-zA-Z_])";

//variable surrounded by spaces or at the end of a line
string newStatement;
string pattern = "(\\s)" + _target.IdentifierName + "(\\s|\\z)";
string replacement = "$1" + _localCopyVariableName + "$2";
Regex rgx = new Regex(pattern);
newStatement = rgx.Replace(input, replacement);

//variable starts the line
replacement = _localCopyVariableName + "$1";
pattern = "^" + _target.IdentifierName + noAdjacentLettersNumbersOrUnderscores;
rgx = new Regex(pattern);
newStatement = rgx.Replace(newStatement, replacement);

//variable name is surrounded by braces, brackets, etc
pattern = noAdjacentLettersNumbersOrUnderscores + _target.IdentifierName + noAdjacentLettersNumbersOrUnderscores;
replacement = "$1" + _localCopyVariableName + "$2";
rgx = new Regex(pattern);
newStatement = rgx.Replace(newStatement, replacement);

return newStatement;
}
private string BuildLocalCopyDeclaration()
{
return Tokens.Dim + " " + _localCopyVariableName + " " + Tokens.As
Expand All @@ -133,40 +151,36 @@ private string BuildLocalCopyAssignment()
return (SymbolList.ValueTypes.Contains(_target.AsTypeName) ? string.Empty : Tokens.Set + " ")
+ _localCopyVariableName + " = " + _target.IdentifierName;
}

private string[] GetModuleLines()
private string[] GetAllModuleLines()
{
var module = Selection.QualifiedName.Component.CodeModule;
var lines = module.GetLines(1, module.CountOfLines);
var moduleContent = Context.Start.InputStream.ToString();
string[] newLine = { "\r\n" };
return lines.Split(newLine, StringSplitOptions.None);
return moduleContent.Split(newLine, StringSplitOptions.None);
}

private string[] GetMethodLines()
private string[] GetProcedureLines()
{
var zbIndex = Selection.Selection.StartLine - 1;
var allLines = GetModuleLines();
var parserRuleCtxt = (Antlr4.Runtime.ParserRuleContext)Context.Parent.Parent;

var endStatement = GetEndOfScopeStatementForDeclaration(allLines[zbIndex]);
int startLine = parserRuleCtxt.Start.Line;
int endLine = parserRuleCtxt.Stop.Line;

var isInScope = true;
var codeBlockLines = new List<string>();
for ( ; isInScope && zbIndex < allLines.Count(); zbIndex++)
var moduleContent = Context.Start.InputStream.ToString();
string[] newLine = { "\r\n" };
var moduleLines = moduleContent.Split(newLine, StringSplitOptions.None);

var procLines = new List<string>();
for( int zbIndex = startLine - 1; zbIndex < endLine; zbIndex++)
{
codeBlockLines.Add(allLines[zbIndex]);
isInScope = !allLines[zbIndex].Contains(endStatement);
procLines.Add(moduleLines[zbIndex]);
}
return codeBlockLines.ToArray();
}

private string GetEndOfScopeStatementForDeclaration(string declaration)
{
return declaration.Contains("Sub ") ? "End Sub" : "End Function";
return procLines.ToArray();
}

private string AutoSuggestedName()
private string SuggestedName()
{
return "local" + _target.IdentifierName.CapitalizeFirstLetter();
return "x" + _target.IdentifierName.CapitalizeFirstLetter();
}
}
}
Loading