Skip to content

Commit 15a9f3f

Browse files
authored
Merge pull request #218 from rubberduck-vba/next
sync with main repo
2 parents 4288c28 + 75be978 commit 15a9f3f

27 files changed

+493
-160
lines changed

RetailCoder.VBE/App.cs

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
using System.Windows.Forms;
1616
using Rubberduck.UI.Command;
1717
using Rubberduck.UI.Command.MenuItems.CommandBars;
18+
using Rubberduck.VBEditor.SafeComWrappers;
1819
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
20+
using Rubberduck.VBEditor.SafeComWrappers.MSForms;
21+
using Rubberduck.VBEditor.SafeComWrappers.Office.Core.Abstract;
1922
using Rubberduck.VersionCheck;
2023

2124
namespace Rubberduck
@@ -88,26 +91,76 @@ private void _hooks_MessageReceived(object sender, HookEventArgs e)
8891

8992
private void RefreshSelection()
9093
{
94+
95+
var caption = String.Empty;
96+
var refCount = 0;
97+
98+
WindowKind windowKind = _vbe.ActiveWindow.Type;
9199
var pane = _vbe.ActiveCodePane;
100+
var component = _vbe.SelectedVBComponent;
101+
102+
Declaration selectedDeclaration = null;
103+
104+
//TODO - I doubt this is the best way to check if the SelectedVBComponent and the ActiveCodePane are the same component.
105+
if (windowKind == WindowKind.CodeWindow || (!_vbe.SelectedVBComponent.IsWrappingNullReference
106+
&& component.ParentProject.ProjectId == pane.CodeModule.Parent.ParentProject.ProjectId
107+
&& component.Name == pane.CodeModule.Parent.Name))
92108
{
93-
Declaration selectedDeclaration = null;
94-
if (!pane.IsWrappingNullReference)
109+
selectedDeclaration = _parser.State.FindSelectedDeclaration(pane);
110+
refCount = selectedDeclaration == null ? 0 : selectedDeclaration.References.Count();
111+
caption = _stateBar.GetContextSelectionCaption(_vbe.ActiveCodePane, selectedDeclaration);
112+
}
113+
else if (windowKind == WindowKind.Designer)
114+
{
115+
caption = GetComponentControlsCaption(component);
116+
}
117+
else
118+
{
119+
if (_vbe.SelectedVBComponent.IsWrappingNullReference)
95120
{
96-
selectedDeclaration = _parser.State.FindSelectedDeclaration(pane);
97-
var refCount = selectedDeclaration == null ? 0 : selectedDeclaration.References.Count();
98-
var caption = _stateBar.GetContextSelectionCaption(_vbe.ActiveCodePane, selectedDeclaration);
99-
_stateBar.SetContextSelectionCaption(caption, refCount);
121+
//The user might have selected the project node in Project Explorer
122+
//If they've chosen a folder, we'll return the project anyway
123+
caption = !_vbe.ActiveVBProject.IsWrappingNullReference
124+
? _vbe.ActiveVBProject.Name
125+
: null;
100126
}
101-
102-
var currentStatus = _parser.State.Status;
103-
if (ShouldEvaluateCanExecute(selectedDeclaration, currentStatus))
127+
else
104128
{
105-
_appMenus.EvaluateCanExecute(_parser.State);
106-
_stateBar.EvaluateCanExecute(_parser.State);
129+
caption = component.Type == ComponentType.UserForm && component.HasOpenDesigner
130+
? GetComponentControlsCaption(component)
131+
: String.Format("{0}.{1} ({2})", component.ParentProject.Name, component.Name, component.Type);
107132
}
133+
}
134+
135+
_stateBar.SetContextSelectionCaption(caption, refCount);
136+
137+
var currentStatus = _parser.State.Status;
138+
if (ShouldEvaluateCanExecute(selectedDeclaration, currentStatus))
139+
{
140+
_appMenus.EvaluateCanExecute(_parser.State);
141+
_stateBar.EvaluateCanExecute(_parser.State);
142+
}
108143

109-
_lastStatus = currentStatus;
110-
_lastSelectedDeclaration = selectedDeclaration;
144+
_lastStatus = currentStatus;
145+
_lastSelectedDeclaration = selectedDeclaration;
146+
}
147+
148+
private string GetComponentControlsCaption(IVBComponent component)
149+
{
150+
switch (component.SelectedControls.Count)
151+
{
152+
case 0:
153+
//TODO get the real designer for VB6
154+
return String.Format("{0}.{1} ({2})", component.ParentProject.Name, component.Name, "MSForms.UserForm");
155+
break;
156+
case 1:
157+
//TODO return the libraryName.className of the control
158+
IControl control = component.SelectedControls.First();
159+
return String.Format("{0}.{1}.{2} ({3})", component.ParentProject.Name, component.Name, control.Name, control.TypeName());
160+
break;
161+
default:
162+
return String.Format("{0}.{1} ({2})", component.ParentProject.Name, component.Name, RubberduckUI.ContextMultipleControlsSelection);
163+
break;
111164
}
112165
}
113166

RetailCoder.VBE/Common/RubberduckHooks.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ private IntPtr WindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam)
220220
}
221221
break;
222222
case WM.CLOSE:
223+
case WM.RUBBERDUCK_SINKING:
224+
suppress = true;
223225
Detach();
224226
break;
225227
}
Lines changed: 102 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using Rubberduck.Inspections.Abstract;
22
using System;
3-
using System.Collections.Generic;
43
using System.Linq;
54
using Rubberduck.Parsing;
65
using Rubberduck.VBEditor;
@@ -10,118 +9,137 @@
109
using System.Windows.Forms;
1110
using Rubberduck.UI.Refactorings;
1211
using Rubberduck.Common;
12+
using System.Text.RegularExpressions;
13+
using System.Collections.Generic;
1314

1415
namespace Rubberduck.Inspections.QuickFixes
1516
{
1617
public class AssignedByValParameterQuickFix : QuickFixBase
1718
{
1819
private readonly Declaration _target;
1920
private string _localCopyVariableName;
20-
private bool _forceUseOfSuggestedName;
21-
private readonly string[] _originalCodeLines;
21+
private bool _isQuickFixUnitTest;
22+
private string[] _originalProcCodeLines;
2223

2324
public AssignedByValParameterQuickFix(Declaration target, QualifiedSelection selection)
2425
: base(target.Context, selection, InspectionsUI.AssignedByValParameterQuickFix)
2526
{
2627
_target = target;
27-
_forceUseOfSuggestedName = false;
28-
_localCopyVariableName = string.Empty;
29-
30-
_originalCodeLines = GetMethodLines();
28+
_isQuickFixUnitTest = false;
29+
_localCopyVariableName = SuggestedName();
30+
_originalProcCodeLines = GetProcedureLines();
3131
}
3232

3333
public override bool CanFixInModule { get { return false; } }
3434
public override bool CanFixInProject { get { return false; } }
3535

36-
//This function exists solely to support unit testing - by preventing the popup dialog
36+
//This function exists solely to support unit testing
3737
public void TESTONLY_FixUsingAutoGeneratedName()
3838
{
39-
_forceUseOfSuggestedName = true;
39+
//Prevent the popup dialog and forces the use of the AutoSuggestedName
40+
_isQuickFixUnitTest = true;
41+
4042
Fix();
4143
}
4244

4345
public override void Fix()
4446
{
45-
if (_forceUseOfSuggestedName)
46-
{
47-
_localCopyVariableName = AutoSuggestedName();
48-
IsCancelled = false;
49-
}
50-
else
51-
{
52-
GetLocalCopyVariableNameFromUser();
53-
}
5447

55-
if (!IsCancelled)
56-
{
57-
ModifyBlockToUseLocalCopyVariable();
58-
}
48+
SetLocalCopyVariableName();
49+
50+
if (IsCancelled) { return; }
51+
52+
ModifyBlockToUseLocalCopyVariable();
5953
}
6054

61-
private void GetLocalCopyVariableNameFromUser()
55+
private void SetLocalCopyVariableName()
6256
{
63-
using (var view = new AssignedByValParameterQuickFixDialog(_originalCodeLines))
57+
using (var view = new AssignedByValParameterQuickFixDialog(_originalProcCodeLines))
6458
{
6559
view.Target = _target;
66-
view.NewName = AutoSuggestedName();
67-
view.ShowDialog();
68-
69-
IsCancelled = view.DialogResult == DialogResult.Cancel;
70-
if (!IsCancelled) { _localCopyVariableName = view.NewName; }
60+
view.NewName = SuggestedName();
61+
if (!_isQuickFixUnitTest)
62+
{
63+
view.ShowDialog();
64+
IsCancelled = view.DialogResult == DialogResult.Cancel;
65+
}
66+
if (!IsCancelled)
67+
{
68+
_localCopyVariableName = view.NewName;
69+
}
7170
}
7271
}
7372

7473
private void ModifyBlockToUseLocalCopyVariable()
7574
{
76-
if(ProposedNameIsInUse()) { return; }
75+
if(!CheckLocalVariableNameIsValidForUpdate())
76+
{
77+
return;
78+
}
7779

78-
var module = Selection.QualifiedName.Component.CodeModule;
79-
var startLine = Selection.Selection.StartLine;
80+
ReplaceAssignedByValParameterReferences();
8081

81-
module.InsertLines(++startLine, BuildLocalCopyDeclaration());
82-
module.InsertLines(++startLine, BuildLocalCopyAssignment());
83-
var moduleLines = GetModuleLines();
84-
//moduleLines array index is zero-based
85-
var endOfScopeStatement = GetEndOfScopeStatementForDeclaration(moduleLines[Selection.Selection.StartLine - 1]);
86-
87-
var isInScope = true;
88-
int zbIndex; //Zero-Based index for moduleLines array
89-
//starts with lines after the above inserts
90-
for (zbIndex = startLine ; isInScope && zbIndex < module.CountOfLines; zbIndex++)
82+
AddDeclarationAndAssignment();
83+
}
84+
private bool CheckLocalVariableNameIsValidForUpdate()
85+
{
86+
if (_localCopyVariableName.Equals(string.Empty))
9187
{
92-
var obIndex = zbIndex + 1; //One-Based index for module object
93-
if (LineRequiresUpdate(moduleLines[zbIndex]))
94-
{
95-
var newStatement = moduleLines[zbIndex].Replace(_target.IdentifierName, _localCopyVariableName);
96-
module.ReplaceLine(obIndex, newStatement);
97-
}
98-
isInScope = !moduleLines[zbIndex].Contains(endOfScopeStatement);
88+
return false;
89+
}
90+
var validator = new VariableNameValidator(_localCopyVariableName);
91+
if (_originalProcCodeLines.Any(c => validator.IsFoundIn(c)))
92+
{
93+
return false;
9994
}
95+
return validator.IsValidName();
10096
}
10197

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

107-
private bool LineRequiresUpdate(string line)
110+
private void AddDeclarationAndAssignment()
108111
{
109-
return line.Contains(" " + _target.IdentifierName + " ")
110-
|| line.Contains(NameAsLeftHandSide())
111-
|| line.Contains(NameAsRightHandSide())
112-
|| line.Contains(NameAsObjectMethodOrAccessorCall())
113-
|| line.Contains(NameAsSubOrFunctionParam())
114-
|| line.Contains(NameAsSubOrFunctionParamFirst())
115-
|| line.Contains(NameAsSubOrFunctionParamLast());
112+
var startLine = Selection.Selection.StartLine;
113+
var module = Selection.QualifiedName.Component.CodeModule;
114+
module.InsertLines(++startLine, BuildLocalCopyDeclaration());
115+
module.InsertLines(++startLine, BuildLocalCopyAssignment());
116116
}
117117

118-
private string NameAsLeftHandSide() { return _target.IdentifierName + " "; }
119-
private string NameAsRightHandSide() { return " " + _target.IdentifierName; }
120-
private string NameAsObjectMethodOrAccessorCall() { return " " + _target.IdentifierName + "."; }
121-
private string NameAsSubOrFunctionParam() { return _target.IdentifierName + ","; }
122-
private string NameAsSubOrFunctionParamFirst() { return "(" + _target.IdentifierName; }
123-
private string NameAsSubOrFunctionParamLast() { return _target.IdentifierName + ")"; }
124-
118+
private string ReplaceByValReferencesInLine(string input)
119+
{
120+
const string noAdjacentLettersNumbersOrUnderscores = "([^0-9a-zA-Z_])";
121+
122+
//variable surrounded by spaces or at the end of a line
123+
string newStatement;
124+
string pattern = "(\\s)" + _target.IdentifierName + "(\\s|\\z)";
125+
string replacement = "$1" + _localCopyVariableName + "$2";
126+
Regex rgx = new Regex(pattern);
127+
newStatement = rgx.Replace(input, replacement);
128+
129+
//variable starts the line
130+
replacement = _localCopyVariableName + "$1";
131+
pattern = "^" + _target.IdentifierName + noAdjacentLettersNumbersOrUnderscores;
132+
rgx = new Regex(pattern);
133+
newStatement = rgx.Replace(newStatement, replacement);
134+
135+
//variable name is surrounded by braces, brackets, etc
136+
pattern = noAdjacentLettersNumbersOrUnderscores + _target.IdentifierName + noAdjacentLettersNumbersOrUnderscores;
137+
replacement = "$1" + _localCopyVariableName + "$2";
138+
rgx = new Regex(pattern);
139+
newStatement = rgx.Replace(newStatement, replacement);
140+
141+
return newStatement;
142+
}
125143
private string BuildLocalCopyDeclaration()
126144
{
127145
return Tokens.Dim + " " + _localCopyVariableName + " " + Tokens.As
@@ -133,40 +151,36 @@ private string BuildLocalCopyAssignment()
133151
return (SymbolList.ValueTypes.Contains(_target.AsTypeName) ? string.Empty : Tokens.Set + " ")
134152
+ _localCopyVariableName + " = " + _target.IdentifierName;
135153
}
136-
137-
private string[] GetModuleLines()
154+
155+
private string[] GetAllModuleLines()
138156
{
139-
var module = Selection.QualifiedName.Component.CodeModule;
140-
var lines = module.GetLines(1, module.CountOfLines);
157+
var moduleContent = Context.Start.InputStream.ToString();
141158
string[] newLine = { "\r\n" };
142-
return lines.Split(newLine, StringSplitOptions.None);
159+
return moduleContent.Split(newLine, StringSplitOptions.None);
143160
}
144161

145-
private string[] GetMethodLines()
162+
private string[] GetProcedureLines()
146163
{
147-
var zbIndex = Selection.Selection.StartLine - 1;
148-
var allLines = GetModuleLines();
164+
var parserRuleCtxt = (Antlr4.Runtime.ParserRuleContext)Context.Parent.Parent;
149165

150-
var endStatement = GetEndOfScopeStatementForDeclaration(allLines[zbIndex]);
166+
int startLine = parserRuleCtxt.Start.Line;
167+
int endLine = parserRuleCtxt.Stop.Line;
151168

152-
var isInScope = true;
153-
var codeBlockLines = new List<string>();
154-
for ( ; isInScope && zbIndex < allLines.Count(); zbIndex++)
169+
var moduleContent = Context.Start.InputStream.ToString();
170+
string[] newLine = { "\r\n" };
171+
var moduleLines = moduleContent.Split(newLine, StringSplitOptions.None);
172+
173+
var procLines = new List<string>();
174+
for( int zbIndex = startLine - 1; zbIndex < endLine; zbIndex++)
155175
{
156-
codeBlockLines.Add(allLines[zbIndex]);
157-
isInScope = !allLines[zbIndex].Contains(endStatement);
176+
procLines.Add(moduleLines[zbIndex]);
158177
}
159-
return codeBlockLines.ToArray();
160-
}
161-
162-
private string GetEndOfScopeStatementForDeclaration(string declaration)
163-
{
164-
return declaration.Contains("Sub ") ? "End Sub" : "End Function";
178+
return procLines.ToArray();
165179
}
166180

167-
private string AutoSuggestedName()
181+
private string SuggestedName()
168182
{
169-
return "local" + _target.IdentifierName.CapitalizeFirstLetter();
183+
return "x" + _target.IdentifierName.CapitalizeFirstLetter();
170184
}
171185
}
172186
}

0 commit comments

Comments
 (0)