@@ -70,8 +70,6 @@ private void Setup()
7070 throw new InvalidTargetSelectionException ( firstSelection , message ) ;
7171 }
7272
73- SelectedCode = string . Join ( Environment . NewLine , SelectedContexts . Select ( c => c . GetText ( ) ) ) ;
74-
7573 var declarationsInSelection = GetDeclarationsInSelection ( QualifiedSelection ) ;
7674
7775 var sourceMethodParameters = ( ( IParameterizedDeclaration ) TargetMethod ) . Parameters ;
@@ -119,9 +117,12 @@ private void Setup()
119117
120118 private IOrderedEnumerable < Declaration > GetDeclarationsInSelection ( QualifiedSelection qualifiedSelection )
121119 {
120+ //Had to add check for declaration type name is VariableDeclaration despite already filtering on
121+ //DeclarationType.Variable. This is due to Debug.Print being picked up for some reason!
122122 return _declarationFinderProvider . DeclarationFinder . UserDeclarations ( DeclarationType . Variable )
123123 . Where ( d => qualifiedSelection . Selection . Contains ( d . Selection ) &&
124- d . QualifiedName . QualifiedModuleName == qualifiedSelection . QualifiedName )
124+ d . QualifiedName . QualifiedModuleName == qualifiedSelection . QualifiedName &&
125+ d . GetType ( ) . Name == "VariableDeclaration" )
125126 . OrderBy ( d => d . Selection . StartLine )
126127 . ThenBy ( d => d . Selection . StartColumn ) ;
127128 }
@@ -166,37 +167,41 @@ private void SetUpParameters(IEnumerable<Declaration> inboundVariables, IEnumera
166167 }
167168 }
168169
169- public string SelectedCode { get ; private set ; }
170170 private int SelectionIndentation ;
171171
172172 //Code excluding declarations that are to be moved out of the selection
173173 public string SelectedCodeToExtract
174174 {
175175 get
176176 {
177- //TODO *** ANY WAY TO USE MOVE CLOSER TO USAGE REFACTORING CODE??? ***
178-
179177 var targetMethodCode = TargetMethod . Context . GetText ( ) . Split ( new [ ] { Environment . NewLine } , StringSplitOptions . None ) ;
180- //var originalCode = string.Join(Environment.NewLine, SelectedContexts.Select(c => c.GetText()));
181- //TODO - create way to map selection to the string or confirm that commented out code above works (including whitespace)
182-
183178 var targetMethodSelection = TargetMethod . Selection ;
184179 var selectionToExtract = QualifiedSelection . Selection ;
185180 var selectionCode = targetMethodCode . Skip ( selectionToExtract . StartLine - targetMethodSelection . StartLine )
186181 . Take ( selectionToExtract . EndLine - selectionToExtract . StartLine + 1 )
187182 . ToList ( ) ;
188183 SelectionIndentation = selectionCode [ 0 ] . Length - selectionCode [ 0 ] . TrimStart ( ' ' ) . Length ;
189- //Handle first and last lines of selection.
190- //As a start, detect if code on those lines that is not in the selection and throw error
184+
185+ //Remove any code on the first and last lines of selection that isn't inside the selection
186+ var firstLineStartExcludedTo = selectionToExtract . StartColumn - 1 ;
187+ var firstLineWasPruned = false ;
191188 if ( selectionToExtract . StartColumn > 1 )
192189 {
193- selectionCode [ 0 ] = selectionCode [ 0 ] . Substring ( selectionToExtract . StartColumn - 1 ) ;
190+ selectionCode [ 0 ] = selectionCode [ 0 ] . Substring ( firstLineStartExcludedTo ) ;
191+ firstLineWasPruned = true ;
194192 }
195- if ( selectionToExtract . EndColumn < targetMethodCode [ selectionToExtract . EndLine - selectionToExtract . StartLine ] . Length )
193+ var lastLineEndExcludedFrom = selectionToExtract . EndColumn - 1 ;
194+ if ( firstLineWasPruned && selectionToExtract . StartLine == selectionToExtract . EndLine )
196195 {
197- selectionCode [ selectionCode . Count - 1 ] = selectionCode [ selectionCode . Count - 1 ] . Substring ( 0 , selectionToExtract . EndColumn - 1 ) ;
196+ lastLineEndExcludedFrom -= firstLineStartExcludedTo ;
198197 }
199-
198+ var lastLineWasPruned = false ;
199+ if ( selectionToExtract . EndColumn < targetMethodCode [ selectionToExtract . EndLine - targetMethodSelection . StartLine ] . Length )
200+ {
201+ selectionCode [ selectionCode . Count - 1 ] = selectionCode [ selectionCode . Count - 1 ] . Substring ( 0 , lastLineEndExcludedFrom ) ;
202+ lastLineWasPruned = true ;
203+ }
204+ //Remove code for declarations that need to be moved out of the selection to stay in the parent method
200205 foreach ( var decl in _declarationsToMoveOut . Except ( _declarationsToMoveOut . Where ( d => d . IdentifierName == ReturnParameter . Name ) ) )
201206 {
202207 var variableListStmt = ( VBAParser . VariableListStmtContext ) decl . Context . Parent ;
@@ -218,24 +223,80 @@ public string SelectedCodeToExtract
218223 throw new UnableToMoveVariableDeclarationException ( decl ) ;
219224 }
220225
221- //TODO - Check if multiple block statements on the same line
222- //1) Go up to Block ancester
223- //2) Find BlockStmt enclosing the declaration
224- //3) Check if preceding or following BlockStmts (if exist) are on the same line (preceding.Stop or following.Start)
225- //4) preceding blocks seem okay except for indentation. following blocks
226-
227-
226+ int declLineStartPrunedAmount = ( startLine == selectionToExtract . StartLine && firstLineWasPruned ) ? firstLineStartExcludedTo : 0 ;
227+ int declLineEndPrunedAmount = ( startLine == selectionToExtract . EndLine && lastLineWasPruned ) ? lastLineEndExcludedFrom : 0 ;
228228 //Remove declaration range from selected code
229- selectionCode . RemoveAt ( startLine - selectionToExtract . StartLine ) ;
229+ RemoveDeclaration ( decl , selectionCode , startLine - selectionToExtract . StartLine , declLineStartPrunedAmount , declLineEndPrunedAmount ) ;
230230 }
231231
232- //var selectionsToMoveOut = (from dec in _declarationsToMoveOut
233- // orderby dec.Selection.StartLine, dec.Selection.StartColumn
234- // select dec.Selection);
235232 return string . Join ( Environment . NewLine , selectionCode ) ;
236233 }
237234 }
238235
236+ private void RemoveDeclaration ( Declaration decl , List < string > selectionCode , int declCodeIndex , int declLineStartPrunedAmount , int declLineEndPrunedAmount )
237+ {
238+ //Search for other BlockStatements on the same line if need to just cut out the declaration
239+ string leftPart = string . Empty ;
240+ string rightPart = string . Empty ;
241+ int cutFrom = 0 ;
242+ int cutTo = selectionCode [ declCodeIndex ] . Length ;
243+ var declBlockStmtContext = decl . Context . GetAncestor < VBAParser . BlockStmtContext > ( ) ;
244+ VBAParser . EndOfStatementContext precedingContext ;
245+ VBAParser . BlockStmtContext precedingBlockStmtContext ;
246+ bool hasPrior = false ;
247+ if ( declBlockStmtContext . TryGetPrecedingContext ( out precedingContext ) )
248+ {
249+ if ( precedingContext . TryGetPrecedingContext ( out precedingBlockStmtContext ) )
250+ {
251+ if ( precedingBlockStmtContext . Stop . Line == declBlockStmtContext . Start . Line &&
252+ QualifiedSelection . Contains ( new QualifiedSelection ( QualifiedSelection . QualifiedName , precedingBlockStmtContext . GetSelection ( ) ) ) )
253+ {
254+ hasPrior = true ;
255+ //start of cut to exclude end of statement (i.e. colon separator) following previous block statement
256+ cutFrom = precedingBlockStmtContext . Stop . EndColumn ( ) ;
257+ leftPart = selectionCode [ declCodeIndex ] . Substring ( 0 , cutFrom ) ;
258+ }
259+ }
260+ }
261+
262+ VBAParser . EndOfStatementContext followingContext ;
263+ VBAParser . BlockStmtContext followingBlockStmtContext ;
264+ bool hasFollower = false ;
265+ if ( declBlockStmtContext . TryGetFollowingContext ( out followingContext ) )
266+ {
267+ if ( followingContext . TryGetFollowingContext ( out followingBlockStmtContext ) )
268+ {
269+ if ( followingBlockStmtContext . Start . Line == declBlockStmtContext . Stop . Line &&
270+ QualifiedSelection . Contains ( new QualifiedSelection ( QualifiedSelection . QualifiedName , followingBlockStmtContext . GetSelection ( ) ) ) )
271+ {
272+ hasFollower = true ;
273+ //end of cut to just go up to end of block statement to be cut, preserving following end of statement
274+ //unless the declaration to move was the first block statement of the line and so we need to remove
275+ //the end of statement context too to avoid starting a line with a colon
276+ if ( hasPrior )
277+ {
278+ cutTo = declBlockStmtContext . Stop . EndColumn ( ) - declLineStartPrunedAmount ;
279+ }
280+ else
281+ {
282+ cutTo = followingContext . Stop . EndColumn ( ) - declLineStartPrunedAmount ;
283+ }
284+ rightPart = selectionCode [ declCodeIndex ] . Substring ( cutTo , selectionCode [ declCodeIndex ] . Length - cutTo ) ;
285+ }
286+ }
287+ }
288+
289+ if ( hasFollower || hasPrior )
290+ {
291+ selectionCode [ declCodeIndex ] = leftPart + rightPart ;
292+ }
293+ else
294+ {
295+ //No other code found, remove whole line
296+ selectionCode . RemoveAt ( declCodeIndex ) ;
297+ }
298+ }
299+
239300 public ObservableCollection < ExtractMethodParameter > Parameters { get ; set ; }
240301
241302 private IEnumerable < ExtractMethodParameter > ArgumentsToPass => from p in Parameters where p != ReturnParameter select p ;
0 commit comments