Skip to content

Commit 2a0e499

Browse files
committed
Fix selection overlaps method and enhance ability to deal with trickier selections
1 parent cb5208a commit 2a0e499

File tree

10 files changed

+409
-372
lines changed

10 files changed

+409
-372
lines changed

Rubberduck.Parsing/Grammar/VBAParserGenericVisitor.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

Rubberduck.Parsing/SelectionExtensions.cs

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -68,39 +68,51 @@ public static bool IsContainedIn(this Selection selection, ParserRuleContext con
6868
/// <returns>Boolean with true indicating that the selections overlaps</returns>
6969
public static bool Overlaps(this Selection thisSelection, Selection selection)
7070
{
71-
if (thisSelection.StartLine == selection.EndLine)
72-
{
73-
if (thisSelection.StartColumn <= selection.EndColumn)
74-
return true;
75-
}
76-
else if(thisSelection.EndLine == selection.EndLine)
77-
{
78-
if (thisSelection.EndColumn <= selection.EndColumn)
79-
return true;
80-
}
81-
82-
if (thisSelection.EndLine == selection.StartLine)
83-
{
84-
if (thisSelection.EndColumn >= selection.StartColumn)
85-
return true;
86-
}
87-
else if(thisSelection.StartLine == selection.StartLine)
88-
{
89-
if (thisSelection.StartColumn <= selection.StartColumn)
90-
return true;
91-
}
92-
93-
if (thisSelection.StartLine < selection.EndLine && thisSelection.EndLine > selection.StartLine)
94-
{
71+
var startFirstSelection = new Selection(thisSelection.StartLine, thisSelection.StartColumn);
72+
var endFirstSelection = new Selection(thisSelection.EndLine, thisSelection.EndColumn);
73+
if (selection.Contains(startFirstSelection) || selection.Contains(endFirstSelection))
9574
return true;
96-
}
9775

98-
if (thisSelection.EndLine > selection.StartLine && thisSelection.StartLine < selection.EndLine)
99-
{
76+
var startSecondSelection = new Selection(selection.StartLine, selection.StartColumn);
77+
var endSecondSelection = new Selection(selection.EndLine, selection.EndColumn);
78+
if (thisSelection.Contains(startSecondSelection) || thisSelection.Contains(endSecondSelection))
10079
return true;
101-
}
10280

103-
return false;
81+
return false;
82+
83+
//if (thisSelection.StartLine == selection.EndLine)
84+
//{
85+
// if (thisSelection.StartColumn <= selection.EndColumn)
86+
// return true;
87+
//}
88+
//else if(thisSelection.EndLine == selection.EndLine)
89+
//{
90+
// if (thisSelection.EndColumn <= selection.EndColumn)
91+
// return true;
92+
//}
93+
94+
//if (thisSelection.EndLine == selection.StartLine)
95+
//{
96+
// if (thisSelection.EndColumn >= selection.StartColumn)
97+
// return true;
98+
//}
99+
//else if(thisSelection.StartLine == selection.StartLine)
100+
//{
101+
// if (thisSelection.StartColumn <= selection.StartColumn)
102+
// return true;
103+
//}
104+
105+
//if (thisSelection.StartLine < selection.EndLine && thisSelection.EndLine > selection.StartLine)
106+
//{
107+
// return true;
108+
//}
109+
110+
//if (thisSelection.EndLine > selection.StartLine && thisSelection.StartLine < selection.EndLine)
111+
//{
112+
// return true;
113+
//}
114+
115+
//return false;
104116
}
105117
}
106118
}

Rubberduck.Refactorings/ExtractMethod/ExtractMethodModel.cs

Lines changed: 87 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -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;

Rubberduck.Refactorings/ExtractMethod/ExtractMethodRefactoring.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ protected override ExtractMethodModel InitializeModel(Declaration target)
6464
else
6565
{
6666
throw new InvalidTargetSelectionException(_selectionProvider.ActiveSelection().GetValueOrDefault(),
67-
Validator.InvalidContexts.FirstOrDefault().Item2);
67+
Validator.InvalidContexts.FirstOrDefault()?.Item2 ?? string.Empty);
6868
}
6969
}
7070

Rubberduck.Refactorings/ExtractMethod/ExtractMethodRefactoringAction.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Linq;
33
using Antlr4.Runtime.Misc;
44
using Rubberduck.Parsing.Rewriter;
5+
using Rubberduck.VBEditor;
56

67
namespace Rubberduck.Refactorings.ExtractMethod
78
{
@@ -24,7 +25,9 @@ public override void Refactor(ExtractMethodModel model, IRewriteSession rewriteS
2425
var selectionInterval = new Interval(startIndex, endIndex);
2526

2627
rewriter.InsertAfter(model.TargetMethod.Context.Stop.TokenIndex, Environment.NewLine + model.NewMethodCode);
27-
rewriter.Replace(selectionInterval, model.ReplacementCode);
28+
rewriter.Replace(selectionInterval, model.ReplacementCode + Environment.NewLine);
29+
30+
rewriter.Selection = new Selection(selection.Selection.StartLine, selection.Selection.StartColumn);
2831
}
2932
}
3033
}

0 commit comments

Comments
 (0)