1
1
using Antlr4 . Runtime ;
2
+ using Antlr4 . Runtime . Tree ;
2
3
using Rubberduck . Inspections . Abstract ;
3
4
using Rubberduck . Inspections . Resources ;
4
5
using Rubberduck . Parsing . Grammar ;
5
6
using Rubberduck . Parsing . Symbols ;
6
7
using Rubberduck . VBEditor ;
7
- using System ;
8
- using System . Collections . Generic ;
9
- using System . Text . RegularExpressions ;
10
8
using static Rubberduck . Parsing . Grammar . VBAParser ;
11
9
12
10
namespace Rubberduck . Inspections . QuickFixes
@@ -17,208 +15,103 @@ namespace Rubberduck.Inspections.QuickFixes
17
15
public class PassParameterByReferenceQuickFix : QuickFixBase
18
16
{
19
17
private Declaration _target ;
20
- private int _byValTokenProcLine ;
21
- private int _byValIdentifierNameProcLine ;
22
18
23
19
public PassParameterByReferenceQuickFix ( Declaration target , QualifiedSelection selection )
24
20
: base ( target . Context , selection , InspectionsUI . PassParameterByReferenceQuickFix )
25
21
{
26
22
_target = target ;
27
- _byValTokenProcLine = 0 ;
28
- _byValIdentifierNameProcLine = 0 ;
29
23
}
30
24
31
25
public override void Fix ( )
32
26
{
33
- string byValTargetString ;
34
- string byValTokenReplacementString ;
35
- string replacementString ;
27
+ var argCtxt = GetArgContextForIdentifier ( Context , _target . IdentifierName ) ;
36
28
37
- var procLines = RetrieveProcedureLines ( ) ;
29
+ var terminalNodeImpl = GetByValNodeForArgCtx ( argCtxt ) ;
38
30
39
- SetMemberLineValues ( procLines ) ;
31
+ var replacementLine = GenerateByRefReplacementLine ( terminalNodeImpl ) ;
40
32
41
- string moduleLineWithByValToken = procLines [ _byValTokenProcLine - 1 ] ;
33
+ ReplaceModuleLine ( terminalNodeImpl . Symbol . Line , replacementLine ) ;
42
34
43
- if ( _byValTokenProcLine == _byValIdentifierNameProcLine )
44
- {
45
- //The replacement is based on the (e.g. "ByVal identifierName")
46
- byValTargetString = Tokens . ByVal + " " + _target . IdentifierName ;
47
- byValTokenReplacementString = BuildByRefParameter ( byValTargetString ) ;
48
- replacementString = moduleLineWithByValToken . Replace ( byValTargetString , byValTokenReplacementString ) ;
49
- }
50
- else
51
- {
52
- //if the token and identifier are on different lines, then the target
53
- //string consists of the ByVal token and the LineContinuation token.
54
- //(e.g. the replacement is based on "ByVal _". Spaces between tokens can vary)
55
- byValTargetString = GetUniqueTargetStringForByValAtEndOfLine ( moduleLineWithByValToken ) ;
56
- byValTokenReplacementString = BuildByRefParameter ( byValTargetString ) ;
57
-
58
- //avoid updating possible cases of ByVal followed by underscore-prefixed identifiers
59
- var index = moduleLineWithByValToken . LastIndexOf ( byValTargetString ) ;
60
- var firstPart = moduleLineWithByValToken . Substring ( 0 , index ) ;
61
- replacementString = firstPart + byValTokenReplacementString ;
62
- }
63
-
64
- var module = Selection . QualifiedName . Component . CodeModule ;
65
- module . ReplaceLine ( RetrieveTheProcedureStartLine ( ) + _byValTokenProcLine - 1 , replacementString ) ;
66
35
}
67
- private string [ ] RetrieveProcedureLines ( )
36
+ private ArgContext GetArgContextForIdentifier ( ParserRuleContext context , string identifier )
68
37
{
69
- var moduleContent = Context . Start . InputStream . ToString ( ) ;
70
- string [ ] newLine = { "\r \n " } ;
71
- var moduleLines = moduleContent . Split ( newLine , StringSplitOptions . None ) ;
72
- var procLines = new List < string > ( ) ;
73
- var startIndex = RetrieveTheProcedureStartLine ( ) ;
74
- var endIndex = RetrieveTheProcedureEndLine ( ) ;
75
- for ( int idx = startIndex - 1 ; idx < endIndex ; idx ++ )
38
+ var procStmtCtx = ( ParserRuleContext ) context . Parent . Parent ;
39
+ var procStmtCtxChildren = procStmtCtx . children ;
40
+ for ( int idx = 0 ; idx < procStmtCtxChildren . Count ; idx ++ )
76
41
{
77
- procLines . Add ( moduleLines [ idx ] ) ;
42
+ if ( procStmtCtxChildren [ idx ] is SubstmtContext )
43
+ {
44
+ var procStmtCtxChild = ( SubstmtContext ) procStmtCtxChildren [ idx ] ;
45
+ var arg = procStmtCtxChild . children ;
46
+ for ( int idx2 = 0 ; idx2 < arg . Count ; idx2 ++ )
47
+ {
48
+ if ( arg [ idx2 ] is ArgContext )
49
+ {
50
+ var name = GetIdentifierNameFor ( ( ArgContext ) arg [ idx2 ] ) ;
51
+ if ( name . Equals ( identifier ) )
52
+ {
53
+ return ( ArgContext ) arg [ idx2 ] ;
54
+ }
55
+ }
56
+ }
57
+ }
78
58
}
79
- return procLines . ToArray ( ) ;
80
- }
81
- private int RetrieveTheProcedureStartLine ( )
82
- {
83
- var parserRuleCtxt = ( ParserRuleContext ) Context . Parent . Parent ;
84
- return parserRuleCtxt . Start . Line ;
85
- }
86
- private int RetrieveTheProcedureEndLine ( )
87
- {
88
- var parserRuleCtxt = ( ParserRuleContext ) Context . Parent . Parent ;
89
- return parserRuleCtxt . Stop . Line ;
90
- }
91
- private string BuildByRefParameter ( string originalParameter )
92
- {
93
- var everythingAfterTheByValToken = originalParameter . Substring ( Tokens . ByVal . Length ) ;
94
- return Tokens . ByRef + everythingAfterTheByValToken ;
59
+ return null ;
95
60
}
96
- private string GetUniqueTargetStringForByValAtEndOfLine ( string procLineWithByValToken )
61
+ private string GetIdentifierNameFor ( ArgContext argCtxt )
97
62
{
98
- System . Diagnostics . Debug . Assert ( procLineWithByValToken . Contains ( Tokens . LineContinuation ) ) ;
99
-
100
- var positionOfLineContinuation = procLineWithByValToken . LastIndexOf ( Tokens . LineContinuation ) ;
101
- var positionOfLastByValToken = procLineWithByValToken . LastIndexOf ( Tokens . ByVal ) ;
102
- return procLineWithByValToken . Substring ( positionOfLastByValToken , positionOfLineContinuation - positionOfLastByValToken + Tokens . LineContinuation . Length ) ;
63
+ var argCtxtChild = argCtxt . children ;
64
+ var idRef = GetUnRestrictedIdentifierCtx ( argCtxt ) ;
65
+ return idRef . GetText ( ) ;
103
66
}
104
- private void SetMemberLineValues ( string [ ] procedureLines )
67
+ private UnrestrictedIdentifierContext GetUnRestrictedIdentifierCtx ( ArgContext argCtxt )
105
68
{
106
-
107
- string line ;
108
- bool byValTokenFound = false ;
109
- bool byValIdentifierNameFound = false ;
110
- for ( int zbIndexByValLine = 0 ; ! byValIdentifierNameFound && zbIndexByValLine < procedureLines . Length ; zbIndexByValLine ++ )
69
+ var argCtxtChild = argCtxt . children ;
70
+ for ( int idx = 0 ; idx < argCtxtChild . Count ; idx ++ )
111
71
{
112
- line = procedureLines [ zbIndexByValLine ] ;
113
- if ( line . Contains ( Tokens . ByVal ) )
72
+ if ( argCtxtChild [ idx ] is UnrestrictedIdentifierContext )
114
73
{
115
- _byValTokenProcLine = zbIndexByValLine + 1 ;
116
- byValTokenFound = true ;
117
- }
118
- if ( byValTokenFound )
119
- {
120
- int lineNum = GetIdentifierLineNumber ( _target . IdentifierName ) ;
121
- if ( lineNum > 0 )
122
- {
123
- _byValIdentifierNameProcLine = lineNum ;
124
- byValIdentifierNameFound = true ;
125
- }
126
- /*
127
- if (line.Contains(_target.IdentifierName))
128
- {
129
- _byValIdentifierNameProcLine = zbIndexByValLine + 1;
130
- byValIdentifierNameFound = true;
131
- }
132
- */
74
+ return ( UnrestrictedIdentifierContext ) argCtxtChild [ idx ] ;
133
75
}
134
76
}
135
-
136
- System . Diagnostics . Debug . Assert ( _byValTokenProcLine > 0 ) ;
137
- System . Diagnostics . Debug . Assert ( _byValIdentifierNameProcLine > 0 ) ;
138
- return ;
77
+ return null ;
139
78
}
140
- private int GetIdentifierLineNumber ( string identifier )
79
+ private TerminalNodeImpl GetByValNodeForArgCtx ( ArgContext argCtxt )
141
80
{
142
- var names = new List < string > ( ) ;
143
- var test = ( SubStmtContext ) Context . Parent . Parent ;
144
- var next = test . children ;
145
- for ( int idx = 0 ; idx < next . Count ; idx ++ )
81
+ var argCtxtChild = argCtxt . children ;
82
+ for ( int idx = 0 ; idx < argCtxtChild . Count ; idx ++ )
146
83
{
147
- if ( next [ idx ] is SubstmtContext )
84
+ if ( argCtxtChild [ idx ] is TerminalNodeImpl )
148
85
{
149
- var child = ( SubstmtContext ) next [ idx ] ;
150
- var arg = child . children ;
151
- for ( int idx2 = 0 ; idx2 < arg . Count ; idx2 ++ )
86
+ var candidate = ( TerminalNodeImpl ) argCtxtChild [ idx ] ;
87
+ if ( candidate . Symbol . Text . Equals ( Tokens . ByVal ) )
152
88
{
153
- if ( arg [ idx2 ] is ArgContext )
154
- {
155
- var asdf = ( ArgContext ) arg [ idx2 ] ;
156
- var kids = asdf . children ;
157
- for ( int idx3 = 0 ; idx3 < kids . Count ; idx3 ++ )
158
- {
159
- var _start = ( ParserRuleContext ) kids [ 0 ] ;
160
- var _stop = ( ParserRuleContext ) kids [ kids . Count - 1 ] ;
161
- int startCol = _start . Start . Column ;
162
- int stopCol = _start . Stop . Column ;
163
-
164
- if ( kids [ idx3 ] is UnrestrictedIdentifierContext )
165
- {
166
- var idRef = ( UnrestrictedIdentifierContext ) kids [ idx3 ] ;
167
- var name = idRef . Start . Text ;
168
- if ( name . Equals ( identifier ) )
169
- {
170
- int lineNum = idRef . Start . Line ;
171
- return lineNum ;
172
- }
173
- }
174
- }
175
- }
89
+ return candidate ;
176
90
}
177
91
}
178
92
}
179
- return - 1 ;
93
+ return null ;
180
94
}
181
- private int GetIdentifierStartIndex ( string identifier , out int stopIndex )
95
+ private string GenerateByRefReplacementLine ( TerminalNodeImpl terminalNodeImpl )
182
96
{
183
- var names = new List < string > ( ) ;
184
- var test = ( SubStmtContext ) Context . Parent . Parent ;
185
- var next = test . children ;
186
- for ( int idx = 0 ; idx < next . Count ; idx ++ )
187
- {
188
- if ( next [ idx ] is SubstmtContext )
189
- {
190
- var child = ( SubstmtContext ) next [ idx ] ;
191
- var arg = child . children ;
192
- for ( int idx2 = 0 ; idx2 < arg . Count ; idx2 ++ )
193
- {
194
- if ( arg [ idx2 ] is ArgContext )
195
- {
196
- var asdf = ( ArgContext ) arg [ idx2 ] ;
197
- var kids = asdf . children ;
198
- for ( int idx3 = 0 ; idx3 < kids . Count ; idx3 ++ )
199
- {
200
- var _start = ( ParserRuleContext ) kids [ 0 ] ;
201
- var _stop = ( ParserRuleContext ) kids [ kids . Count - 1 ] ;
202
- stopIndex = _start . Stop . Column ;
203
- return _start . Start . Column ;
97
+ var module = Selection . QualifiedName . Component . CodeModule ;
98
+ var byValTokenLine = module . GetLines ( terminalNodeImpl . Symbol . Line , 1 ) ;
204
99
205
- if ( kids [ idx3 ] is UnrestrictedIdentifierContext )
206
- {
207
- var idRef = ( UnrestrictedIdentifierContext ) kids [ idx3 ] ;
208
- var name = idRef . Start . Text ;
209
- if ( name . Equals ( identifier ) )
210
- {
211
- int lineNum = idRef . Start . Line ;
212
- return lineNum ;
213
- }
214
- }
215
- }
216
- }
217
- }
218
- }
219
- }
220
- stopIndex = - 1 ;
221
- return - 1 ;
100
+ return ReplaceAtIndex ( byValTokenLine , Tokens . ByVal , Tokens . ByRef , terminalNodeImpl . Symbol . Column ) ;
101
+ }
102
+ private string ReplaceAtIndex ( string input , string toReplace , string replacement , int index )
103
+ {
104
+ int stopIndex = index + toReplace . Length ;
105
+ var prefix = input . Substring ( 0 , index ) ;
106
+ var suffix = input . Substring ( stopIndex + 1 ) ;
107
+ var target = input . Substring ( index , stopIndex - index + 1 ) ;
108
+ return prefix + target . Replace ( toReplace , replacement ) + suffix ;
109
+ }
110
+ private void ReplaceModuleLine ( int lineNumber , string replacementLine )
111
+ {
112
+ var module = Selection . QualifiedName . Component . CodeModule ;
113
+ module . DeleteLines ( lineNumber ) ;
114
+ module . InsertLines ( lineNumber , replacementLine ) ;
222
115
}
223
116
}
224
117
}
0 commit comments