1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Linq ;
4
+ using Rubberduck . Parsing . Grammar ;
5
+ using Rubberduck . Parsing . Symbols ;
6
+ using Rubberduck . Parsing . VBA ;
7
+ using Rubberduck . UI ;
8
+ using Rubberduck . VBEditor ;
9
+
10
+ namespace Rubberduck . Refactorings . MoveCloserToUsage
11
+ {
12
+ public class MoveCloserToUsageRefactoring : IRefactoring
13
+ {
14
+ private readonly List < Declaration > _declarations ;
15
+ private readonly IActiveCodePaneEditor _editor ;
16
+ private readonly IMessageBox _messageBox ;
17
+
18
+ public MoveCloserToUsageRefactoring ( RubberduckParserState parseResult , IActiveCodePaneEditor editor , IMessageBox messageBox )
19
+ {
20
+ _declarations = parseResult . AllDeclarations . ToList ( ) ;
21
+ _editor = editor ;
22
+ _messageBox = messageBox ;
23
+ }
24
+
25
+ public void Refactor ( )
26
+ {
27
+ var qualifiedSelection = _editor . GetSelection ( ) ;
28
+ if ( qualifiedSelection != null )
29
+ {
30
+ Refactor ( FindSelection ( qualifiedSelection . Value ) ) ;
31
+ }
32
+ else
33
+ {
34
+ _messageBox . Show ( "Invalid Selection." , "Rubberduck - Move Closer To Usage" , System . Windows . Forms . MessageBoxButtons . OK ,
35
+ System . Windows . Forms . MessageBoxIcon . Exclamation ) ;
36
+ }
37
+ }
38
+
39
+ public void Refactor ( QualifiedSelection selection )
40
+ {
41
+ Refactor ( FindSelection ( selection ) ) ;
42
+ }
43
+
44
+ public void Refactor ( Declaration target )
45
+ {
46
+ if ( target . DeclarationType != DeclarationType . Variable )
47
+ {
48
+ throw new ArgumentException ( @"Invalid Argument" , "target" ) ;
49
+ }
50
+
51
+ if ( ! target . References . Any ( ) )
52
+ {
53
+ var message = string . Format ( RubberduckUI . MoveCloserToUsage_TargetHasNoReferences , target . IdentifierName ) ;
54
+
55
+ _messageBox . Show ( message , RubberduckUI . MoveCloserToUsage_Caption , System . Windows . Forms . MessageBoxButtons . OK ,
56
+ System . Windows . Forms . MessageBoxIcon . Exclamation ) ;
57
+
58
+ return ;
59
+ }
60
+
61
+ if ( TargetIsReferencedFromMultipleMethods ( target ) )
62
+ {
63
+ var message = string . Format ( RubberduckUI . MoveCloserToUsage_TargetIsUsedInMultipleMethods , target . IdentifierName ) ;
64
+ _messageBox . Show ( message , RubberduckUI . MoveCloserToUsage_Caption , System . Windows . Forms . MessageBoxButtons . OK ,
65
+ System . Windows . Forms . MessageBoxIcon . Exclamation ) ;
66
+
67
+ return ;
68
+ }
69
+
70
+ MoveDeclaration ( target ) ;
71
+ }
72
+
73
+ private bool TargetIsReferencedFromMultipleMethods ( Declaration target )
74
+ {
75
+ var firstReference = target . References . FirstOrDefault ( ) ;
76
+
77
+ return firstReference != null && target . References . Any ( r => r . ParentScope != firstReference . ParentScope ) ;
78
+ }
79
+
80
+ private void MoveDeclaration ( Declaration target )
81
+ {
82
+ InsertDeclaration ( target ) ;
83
+ RemoveVariable ( target ) ;
84
+ }
85
+
86
+ private void InsertDeclaration ( Declaration target )
87
+ {
88
+ var firstReference = target . References . OrderBy ( r => r . Selection . StartLine ) . First ( ) ;
89
+
90
+ var oldLines = _editor . GetLines ( firstReference . Selection ) ;
91
+ var newLines = oldLines . Insert ( firstReference . Selection . StartColumn - 1 , GetDeclarationString ( target ) ) ;
92
+
93
+ _editor . DeleteLines ( firstReference . Selection ) ;
94
+ _editor . InsertLines ( firstReference . Selection . StartLine , newLines ) ;
95
+ }
96
+
97
+ private string GetDeclarationString ( Declaration target )
98
+ {
99
+ return Environment . NewLine + " Dim " + target . IdentifierName + " As " + target . AsTypeName + Environment . NewLine ;
100
+ }
101
+
102
+ private void RemoveVariable ( Declaration target )
103
+ {
104
+ Selection selection ;
105
+ var declarationText = target . Context . GetText ( ) ;
106
+ var multipleDeclarations = HasMultipleDeclarationsInStatement ( target ) ;
107
+
108
+ var variableStmtContext = GetVariableStmtContext ( target ) ;
109
+
110
+ if ( ! multipleDeclarations )
111
+ {
112
+ declarationText = variableStmtContext . GetText ( ) ;
113
+ selection = GetVariableStmtContextSelection ( target ) ;
114
+ }
115
+ else
116
+ {
117
+ selection = new Selection ( target . Context . Start . Line , target . Context . Start . Column ,
118
+ target . Context . Stop . Line , target . Context . Stop . Column ) ;
119
+ }
120
+
121
+ var oldLines = _editor . GetLines ( selection ) ;
122
+
123
+ var newLines = oldLines . Replace ( " _" + Environment . NewLine , string . Empty )
124
+ . Remove ( selection . StartColumn , declarationText . Length ) ;
125
+
126
+ if ( multipleDeclarations )
127
+ {
128
+ selection = GetVariableStmtContextSelection ( target ) ;
129
+ newLines = RemoveExtraComma ( _editor . GetLines ( selection ) . Replace ( oldLines , newLines ) ) ;
130
+ }
131
+
132
+ _editor . DeleteLines ( selection ) ;
133
+
134
+ if ( newLines . Trim ( ) != string . Empty )
135
+ {
136
+ _editor . InsertLines ( selection . StartLine , newLines ) ;
137
+ }
138
+ }
139
+
140
+ private string RemoveExtraComma ( string str )
141
+ {
142
+ if ( str . Count ( c => c == ',' ) == 1 )
143
+ {
144
+ return str . Remove ( str . IndexOf ( ',' ) , 1 ) ;
145
+ }
146
+
147
+ var significantCharacterAfterComma = false ;
148
+
149
+ for ( var index = str . IndexOf ( "Dim" , StringComparison . Ordinal ) + 3 ; index < str . Length ; index ++ )
150
+ {
151
+ if ( ! significantCharacterAfterComma && str [ index ] == ',' )
152
+ {
153
+ return str . Remove ( index , 1 ) ;
154
+ }
155
+
156
+ if ( ! char . IsWhiteSpace ( str [ index ] ) && str [ index ] != '_' && str [ index ] != ',' )
157
+ {
158
+ significantCharacterAfterComma = true ;
159
+ }
160
+
161
+ if ( str [ index ] == ',' )
162
+ {
163
+ significantCharacterAfterComma = false ;
164
+ }
165
+ }
166
+
167
+ return str . Remove ( str . LastIndexOf ( ',' ) , 1 ) ;
168
+ }
169
+
170
+ public Selection GetVariableStmtContextSelection ( Declaration target )
171
+ {
172
+ var statement = GetVariableStmtContext ( target ) ;
173
+
174
+ return new Selection ( statement . Start . Line , statement . Start . Column ,
175
+ statement . Stop . Line , statement . Stop . Column ) ;
176
+ }
177
+
178
+ public VBAParser . VariableStmtContext GetVariableStmtContext ( Declaration target )
179
+ {
180
+ var statement = target . Context . Parent . Parent as VBAParser . VariableStmtContext ;
181
+ if ( statement == null )
182
+ {
183
+ throw new NullReferenceException ( "Statement not found" ) ;
184
+ }
185
+
186
+ return statement ;
187
+ }
188
+
189
+ public bool HasMultipleDeclarationsInStatement ( Declaration target )
190
+ {
191
+ var statement = target . Context . Parent as VBAParser . VariableListStmtContext ;
192
+
193
+ return statement != null && statement . children . Count ( i => i is VBAParser . VariableSubStmtContext ) > 1 ;
194
+ }
195
+
196
+ private Declaration FindSelection ( QualifiedSelection selection )
197
+ {
198
+ var target = _declarations
199
+ . FirstOrDefault ( item => item . IsSelected ( selection ) || item . References . Any ( r => r . IsSelected ( selection ) ) ) ;
200
+
201
+ if ( target != null ) { return target ; }
202
+
203
+ var targets = _declarations . Where ( item => item . ComponentName == selection . QualifiedName . ComponentName ) ;
204
+
205
+ foreach ( var declaration in targets )
206
+ {
207
+ var declarationSelection = new Selection ( declaration . Context . Start . Line ,
208
+ declaration . Context . Start . Column ,
209
+ declaration . Context . Stop . Line ,
210
+ declaration . Context . Stop . Column + declaration . Context . Stop . Text . Length ) ;
211
+
212
+ if ( declarationSelection . Contains ( selection . Selection ) ||
213
+ ! HasMultipleDeclarationsInStatement ( declaration ) && GetVariableStmtContextSelection ( declaration ) . Contains ( selection . Selection ) )
214
+ {
215
+ return declaration ;
216
+ }
217
+
218
+ var reference =
219
+ declaration . References . FirstOrDefault ( r => r . Selection . Contains ( selection . Selection ) ) ;
220
+
221
+ if ( reference != null )
222
+ {
223
+ return reference . Declaration ;
224
+ }
225
+ }
226
+ return null ;
227
+ }
228
+ }
229
+ }
0 commit comments