1
1
using Rubberduck . Inspections . Abstract ;
2
2
using System ;
3
- using System . Collections . Generic ;
4
3
using System . Linq ;
5
4
using Rubberduck . Parsing ;
6
5
using Rubberduck . VBEditor ;
10
9
using System . Windows . Forms ;
11
10
using Rubberduck . UI . Refactorings ;
12
11
using Rubberduck . Common ;
12
+ using System . Text . RegularExpressions ;
13
+ using System . Collections . Generic ;
13
14
14
15
namespace Rubberduck . Inspections . QuickFixes
15
16
{
16
17
public class AssignedByValParameterQuickFix : QuickFixBase
17
18
{
18
19
private readonly Declaration _target ;
19
20
private string _localCopyVariableName ;
20
- private bool _forceUseOfSuggestedName ;
21
- private readonly string [ ] _originalCodeLines ;
21
+ private bool _isQuickFixUnitTest ;
22
+ private string [ ] _originalProcCodeLines ;
22
23
23
24
public AssignedByValParameterQuickFix ( Declaration target , QualifiedSelection selection )
24
25
: base ( target . Context , selection , InspectionsUI . AssignedByValParameterQuickFix )
25
26
{
26
27
_target = target ;
27
- _forceUseOfSuggestedName = false ;
28
- _localCopyVariableName = string . Empty ;
29
-
30
- _originalCodeLines = GetMethodLines ( ) ;
28
+ _isQuickFixUnitTest = false ;
29
+ _localCopyVariableName = SuggestedName ( ) ;
30
+ _originalProcCodeLines = GetProcedureLines ( ) ;
31
31
}
32
32
33
33
public override bool CanFixInModule { get { return false ; } }
34
34
public override bool CanFixInProject { get { return false ; } }
35
35
36
- //This function exists solely to support unit testing - by preventing the popup dialog
36
+ //This function exists solely to support unit testing
37
37
public void TESTONLY_FixUsingAutoGeneratedName ( )
38
38
{
39
- _forceUseOfSuggestedName = true ;
39
+ //Prevent the popup dialog and forces the use of the AutoSuggestedName
40
+ _isQuickFixUnitTest = true ;
41
+
40
42
Fix ( ) ;
41
43
}
42
44
43
45
public override void Fix ( )
44
46
{
45
- if ( _forceUseOfSuggestedName )
46
- {
47
- _localCopyVariableName = AutoSuggestedName ( ) ;
48
- IsCancelled = false ;
49
- }
50
- else
51
- {
52
- GetLocalCopyVariableNameFromUser ( ) ;
53
- }
54
47
55
- if ( ! IsCancelled )
56
- {
57
- ModifyBlockToUseLocalCopyVariable ( ) ;
58
- }
48
+ SetLocalCopyVariableName ( ) ;
49
+
50
+ if ( IsCancelled ) { return ; }
51
+
52
+ ModifyBlockToUseLocalCopyVariable ( ) ;
59
53
}
60
54
61
- private void GetLocalCopyVariableNameFromUser ( )
55
+ private void SetLocalCopyVariableName ( )
62
56
{
63
- using ( var view = new AssignedByValParameterQuickFixDialog ( _originalCodeLines ) )
57
+ using ( var view = new AssignedByValParameterQuickFixDialog ( _originalProcCodeLines ) )
64
58
{
65
59
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
+ }
71
70
}
72
71
}
73
72
74
73
private void ModifyBlockToUseLocalCopyVariable ( )
75
74
{
76
- if ( ProposedNameIsInUse ( ) ) { return ; }
75
+ if ( ! CheckLocalVariableNameIsValidForUpdate ( ) )
76
+ {
77
+ return ;
78
+ }
77
79
78
- var module = Selection . QualifiedName . Component . CodeModule ;
79
- var startLine = Selection . Selection . StartLine ;
80
+ ReplaceAssignedByValParameterReferences ( ) ;
80
81
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 ) )
91
87
{
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 ;
99
94
}
95
+ return validator . IsValidName ( ) ;
100
96
}
101
97
102
- private bool ProposedNameIsInUse ( )
98
+ private void ReplaceAssignedByValParameterReferences ( )
103
99
{
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
+ }
105
108
}
106
109
107
- private bool LineRequiresUpdate ( string line )
110
+ private void AddDeclarationAndAssignment ( )
108
111
{
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 ( ) ) ;
116
116
}
117
117
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
+ }
125
143
private string BuildLocalCopyDeclaration ( )
126
144
{
127
145
return Tokens . Dim + " " + _localCopyVariableName + " " + Tokens . As
@@ -133,40 +151,36 @@ private string BuildLocalCopyAssignment()
133
151
return ( SymbolList . ValueTypes . Contains ( _target . AsTypeName ) ? string . Empty : Tokens . Set + " " )
134
152
+ _localCopyVariableName + " = " + _target . IdentifierName ;
135
153
}
136
-
137
- private string [ ] GetModuleLines ( )
154
+
155
+ private string [ ] GetAllModuleLines ( )
138
156
{
139
- var module = Selection . QualifiedName . Component . CodeModule ;
140
- var lines = module . GetLines ( 1 , module . CountOfLines ) ;
157
+ var moduleContent = Context . Start . InputStream . ToString ( ) ;
141
158
string [ ] newLine = { "\r \n " } ;
142
- return lines . Split ( newLine , StringSplitOptions . None ) ;
159
+ return moduleContent . Split ( newLine , StringSplitOptions . None ) ;
143
160
}
144
161
145
- private string [ ] GetMethodLines ( )
162
+ private string [ ] GetProcedureLines ( )
146
163
{
147
- var zbIndex = Selection . Selection . StartLine - 1 ;
148
- var allLines = GetModuleLines ( ) ;
164
+ var parserRuleCtxt = ( Antlr4 . Runtime . ParserRuleContext ) Context . Parent . Parent ;
149
165
150
- var endStatement = GetEndOfScopeStatementForDeclaration ( allLines [ zbIndex ] ) ;
166
+ int startLine = parserRuleCtxt . Start . Line ;
167
+ int endLine = parserRuleCtxt . Stop . Line ;
151
168
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 ++ )
155
175
{
156
- codeBlockLines . Add ( allLines [ zbIndex ] ) ;
157
- isInScope = ! allLines [ zbIndex ] . Contains ( endStatement ) ;
176
+ procLines . Add ( moduleLines [ zbIndex ] ) ;
158
177
}
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 ( ) ;
165
179
}
166
180
167
- private string AutoSuggestedName ( )
181
+ private string SuggestedName ( )
168
182
{
169
- return "local " + _target . IdentifierName . CapitalizeFirstLetter ( ) ;
183
+ return "x " + _target . IdentifierName . CapitalizeFirstLetter ( ) ;
170
184
}
171
185
}
172
186
}
0 commit comments