Skip to content

Commit 977a51f

Browse files
committed
Split tokens extension into their own class
Fix bugs w/ GetSelection() extension in the ParserRuleContextExtensions.cs Add Contains() and IsContainedIn() extensions Update the tests to cover both ways to check selections
1 parent 94fa38f commit 977a51f

File tree

5 files changed

+104
-70
lines changed

5 files changed

+104
-70
lines changed

Rubberduck.Parsing/ParserRuleContextExtensions.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ public static Selection GetSelection(this ParserRuleContext context)
1313
// if we have an empty module, `Stop` is null
1414
if (context?.Stop == null) { return Selection.Home; }
1515

16-
// ANTLR indexes are 0-based, but VBE's are 1-based.
16+
// ANTLR indexes for columns are 0-based, but VBE's are 1-based.
17+
// ANTLR lines and VBE's lines are both 1-based
1718
// 1 is the default value that will select all lines. Replace zeroes with ones.
1819
// See also: https://msdn.microsoft.com/en-us/library/aa443952(v=vs.60).aspx
1920

2021
return new Selection(context.Start.Line == 0 ? 1 : context.Start.Line,
2122
context.Start.Column + 1,
22-
context.Stop.Line == 0 ? 1 : context.Stop.Line,
23-
context.Stop.Column + context.Stop.Text.Length + 1);
23+
context.Stop.Line == 0 ? 1 : context.Stop.EndLine(),
24+
context.Stop.EndColumn() + 1);
2425
}
2526

2627
//This set of overloads returns the selection for the entire procedure statement body, i.e. Public Function Foo(bar As String) As String

Rubberduck.Parsing/Rubberduck.Parsing.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@
349349
<Compile Include="Symbols\ConstantDeclaration.cs" />
350350
<Compile Include="ComReflection\XmlPersistableDeclarations.cs" />
351351
<Compile Include="Symbols\UnboundMemberDeclaration.cs" />
352+
<Compile Include="TokenExtensions.cs" />
352353
<Compile Include="VBA\AttributeListener.cs" />
353354
<Compile Include="VBA\AttributeParser.cs" />
354355
<Compile Include="VBA\Attributes.cs" />

Rubberduck.Parsing/SelectionExtensions.cs

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static bool Contains(this Selection selection, IToken token)
3232
/// <param name="context">A context which contains several tokens within a module's parse tree</param>
3333
/// <param name="selection">One-based selection, usually from CodePane.Selection</param>
3434
/// <returns>Boolean with true indicating that context is within the selection</returns>
35-
public static bool Contains(this ParserRuleContext context, Selection selection)
35+
public static bool Contains(this Selection selection, ParserRuleContext context)
3636
{
3737
return
3838
(((selection.StartLine == context.Start.Line) && (selection.StartColumn - 1) <= context.Start.Column)
@@ -42,45 +42,14 @@ public static bool Contains(this ParserRuleContext context, Selection selection)
4242
}
4343

4444
/// <summary>
45-
/// Obtain the actual last column the token occupies. Because a token can be spread
46-
/// across multiple lines with line continuations it is necessary to do some work
47-
/// to determine the token's actual ending column.
48-
/// Whitespace and newline should be preserved within the token.
45+
/// Convenience method for validating that a selection is inside a specified parser rule context.
4946
/// </summary>
50-
/// <param name="token">The last token within a given context to test</param>
51-
/// <returns>Zero-based column position</returns>
52-
public static int EndColumn(this IToken token)
47+
/// <param name="selection">The selection that should be contained within the ParserRuleContext</param>
48+
/// <param name="context">The containing ParserRuleContext</param>
49+
/// <returns>Boolean with true indicating that the selection is inside the given context</returns>
50+
public static bool IsContainedIn(this Selection selection, ParserRuleContext context)
5351
{
54-
if (token.Text.Contains(Environment.NewLine))
55-
{
56-
var splitStrings = token.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
57-
var lastOccupiedLine = splitStrings[splitStrings.Length - 1];
58-
59-
return lastOccupiedLine.Length;
60-
}
61-
else
62-
{
63-
return token.Column + token.Text.Length;
64-
}
65-
}
66-
67-
/// <summary>
68-
/// Obtain the actual last line token occupies. Typically it is same as token.Line but
69-
/// when it contains line continuation and is spread across lines, extra newlines are
70-
/// counted and added.
71-
/// </summary>
72-
/// <param name="token"></param>
73-
/// <returns>One-based line position</returns>
74-
public static int EndLine(this IToken token)
75-
{
76-
if(token.Text.Contains(Environment.NewLine))
77-
{
78-
var splitStrings = token.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
79-
80-
return token.Line + (splitStrings.Length - 1);
81-
}
82-
83-
return token.Line;
52+
return context.GetSelection().Contains(selection);
8453
}
8554
}
8655
}

Rubberduck.Parsing/TokenExtensions.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using Antlr4.Runtime;
3+
4+
namespace Rubberduck.Parsing
5+
{
6+
public static class TokenExtensions
7+
{
8+
/// <summary>
9+
/// Obtain the actual last column the token occupies. Because a token can be spread
10+
/// across multiple lines with line continuations it is necessary to do some work
11+
/// to determine the token's actual ending column.
12+
/// Whitespace and newline should be preserved within the token.
13+
/// </summary>
14+
/// <param name="token">The last token within a given context to test</param>
15+
/// <returns>Zero-based column position</returns>
16+
public static int EndColumn(this IToken token)
17+
{
18+
if (token.Text.Contains(Environment.NewLine))
19+
{
20+
var splitStrings = token.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
21+
var lastOccupiedLine = splitStrings[splitStrings.Length - 1];
22+
23+
return lastOccupiedLine.Length;
24+
}
25+
else
26+
{
27+
return token.Column + token.Text.Length;
28+
}
29+
}
30+
31+
/// <summary>
32+
/// Obtain the actual last line token occupies. Typically it is same as token.Line but
33+
/// when it contains line continuation and is spread across lines, extra newlines are
34+
/// counted and added.
35+
/// </summary>
36+
/// <param name="token"></param>
37+
/// <returns>One-based line position</returns>
38+
public static int EndLine(this IToken token)
39+
{
40+
if (token.Text.Contains(Environment.NewLine))
41+
{
42+
var splitStrings = token.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
43+
44+
return token.Line + (splitStrings.Length - 1);
45+
}
46+
47+
return token.Line;
48+
}
49+
}
50+
}

RubberduckTests/Grammar/SelectionExtensionsTests.cs

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ End _
6464
var context = visitor.Visit(tree).First();
6565
var selection = new Selection(3, 0, 10, 5);
6666

67-
Assert.IsFalse(context.Contains(selection));
67+
Assert.IsFalse(selection.Contains(context));
68+
Assert.IsFalse(selection.IsContainedIn(context));
6869
}
6970

7071
[TestMethod]
@@ -92,7 +93,8 @@ End _
9293
var context = visitor.Visit(tree).First();
9394
var selection = new Selection(4, 1, 11, 8);
9495

95-
Assert.IsTrue(context.Contains(selection));
96+
Assert.IsTrue(selection.Contains(context));
97+
Assert.IsFalse(selection.IsContainedIn(context));
9698
}
9799

98100
[TestMethod]
@@ -120,7 +122,8 @@ End _
120122
var context = visitor.Visit(tree).First();
121123
var selection = new Selection(5, 1, 11, 8);
122124

123-
Assert.IsFalse(context.Contains(selection));
125+
Assert.IsFalse(selection.Contains(context));
126+
Assert.IsFalse(selection.IsContainedIn(context));
124127
}
125128

126129
[TestMethod]
@@ -148,7 +151,8 @@ End _
148151
var context = visitor.Visit(tree).First();
149152
var selection = new Selection(4, 1, 10, 8);
150153

151-
Assert.IsFalse(context.Contains(selection));
154+
Assert.IsFalse(selection.Contains(context));
155+
Assert.IsTrue(selection.IsContainedIn(context));
152156
}
153157

154158
[TestMethod]
@@ -175,9 +179,11 @@ End _
175179
var tree = state.GetParseTree(new QualifiedModuleName(component));
176180
var visitor = new SubStmtContextElementCollectorVisitor();
177181
var context = visitor.Visit(tree).First();
178-
pane.Selection = new Selection(4, 1, 11, 8);
182+
pane.Selection = new Selection(4, 1, 11, 6);
179183

180-
Assert.IsTrue(context.Contains(pane.Selection));
184+
Assert.IsTrue(context.GetSelection().Contains(pane.Selection));
185+
Assert.IsTrue(pane.Selection.IsContainedIn(context));
186+
Assert.IsTrue(pane.Selection.Contains(context));
181187
}
182188

183189
[TestMethod]
@@ -203,7 +209,9 @@ Public Sub foo()
203209
var context = visitor.Visit(tree).First();
204210
pane.Selection = new Selection(3, 0, 7, 7);
205211

206-
Assert.IsFalse(context.Contains(pane.Selection));
212+
Assert.IsFalse(context.GetSelection().Contains(pane.Selection));
213+
Assert.IsFalse(pane.Selection.IsContainedIn(context));
214+
Assert.IsFalse(pane.Selection.Contains(context));
207215
}
208216

209217
[TestMethod]
@@ -229,7 +237,9 @@ Public Sub foo()
229237
var context = visitor.Visit(tree).First();
230238
pane.Selection = new Selection(4, 1, 8, 8);
231239

232-
Assert.IsTrue(context.Contains(pane.Selection));
240+
Assert.IsTrue(context.GetSelection().Contains(pane.Selection));
241+
Assert.IsTrue(pane.Selection.IsContainedIn(context));
242+
Assert.IsTrue(pane.Selection.Contains(context));
233243
}
234244

235245
[TestMethod]
@@ -255,13 +265,14 @@ Public Sub foo()
255265
var context = visitor.Visit(tree).First();
256266
var selection = new Selection(4, 1, 8, 8);
257267

258-
Assert.IsTrue(context.Contains(selection));
268+
Assert.IsTrue(selection.Contains(context));
269+
Assert.IsTrue(selection.IsContainedIn(context));
259270
}
260271

261272
[TestMethod]
262273
[TestCategory("Grammar")]
263274
[TestCategory("Selection")]
264-
public void Context_NotIn_Selection_StartTooSoon_OneBased()
275+
public void Context_NotIn_Selection_StartTooLate_OneBased()
265276
{
266277
const string inputCode = @"
267278
Option Explicit
@@ -281,7 +292,8 @@ Public Sub foo()
281292
var context = visitor.Visit(tree).First();
282293
var selection = new Selection(4, 2, 8, 8);
283294

284-
Assert.IsFalse(context.Contains(selection));
295+
Assert.IsFalse(selection.Contains(context));
296+
Assert.IsTrue(selection.IsContainedIn(context));
285297
}
286298

287299
[TestMethod]
@@ -307,7 +319,8 @@ Public Sub foo()
307319
var context = visitor.Visit(tree).First();
308320
var selection = new Selection(4, 1, 8, 7);
309321

310-
Assert.IsFalse(context.Contains(selection));
322+
Assert.IsFalse(selection.Contains(context));
323+
Assert.IsTrue(selection.IsContainedIn(context));
311324
}
312325

313326
[TestMethod]
@@ -342,8 +355,8 @@ End If
342355
var contexts = visitor.Visit(tree);
343356
var selection = new Selection(6, 1, 10, 7);
344357

345-
Assert.IsTrue(contexts.ElementAt(0).Contains(selection)); // first If block
346-
Assert.IsFalse(contexts.ElementAt(1).Contains(selection)); // second If block
358+
Assert.IsTrue(selection.Contains(contexts.ElementAt(0))); // first If block
359+
Assert.IsFalse(selection.Contains(contexts.ElementAt(1))); // second If block
347360
}
348361

349362
[TestMethod]
@@ -379,8 +392,8 @@ End If
379392
var contexts = visitor.Visit(tree);
380393
var selection = new Selection(6, 1, 10, 7);
381394

382-
Assert.IsTrue(contexts.ElementAt(0).Contains(selection)); // first If block
383-
Assert.IsFalse(contexts.ElementAt(1).Contains(selection)); // second If block
395+
Assert.IsTrue(selection.Contains(contexts.ElementAt(0))); // first If block
396+
Assert.IsFalse(selection.Contains(contexts.ElementAt(1))); // second If block
384397
}
385398

386399
[TestMethod]
@@ -416,8 +429,8 @@ End If
416429
var contexts = visitor.Visit(tree);
417430
var selection = new Selection(12, 1, 16, 7);
418431

419-
Assert.IsFalse(contexts.ElementAt(0).Contains(selection)); // first If block
420-
Assert.IsTrue(contexts.ElementAt(1).Contains(selection)); // second If block
432+
Assert.IsFalse(selection.Contains(contexts.ElementAt(0))); // first If block
433+
Assert.IsTrue(selection.Contains(contexts.ElementAt(1))); // second If block
421434
}
422435

423436
[TestMethod]
@@ -455,8 +468,8 @@ End If
455468
var selection = new Selection(12, 1, 16, 7);
456469

457470
Assert.IsTrue(selection.Contains(token)); // last token in second If block
458-
Assert.IsFalse(contexts.ElementAt(0).Contains(selection)); // first If block
459-
Assert.IsTrue(contexts.ElementAt(1).Contains(selection)); // second If block
471+
Assert.IsFalse(selection.Contains(contexts.ElementAt(0))); // first If block
472+
Assert.IsTrue(selection.Contains(contexts.ElementAt(1))); // second If block
460473
}
461474

462475
[TestMethod]
@@ -534,9 +547,9 @@ End If
534547
var selection = new Selection(8, 1, 10, 9);
535548

536549
Assert.IsTrue(selection.Contains(token)); // last token in innermost If block
537-
Assert.IsTrue(contexts.ElementAt(0).Contains(selection)); // innermost If block
538-
Assert.IsFalse(contexts.ElementAt(1).Contains(selection)); // first outer If block
539-
Assert.IsFalse(contexts.ElementAt(2).Contains(selection)); // second outer If block
550+
Assert.IsTrue(selection.Contains(contexts.ElementAt(0))); // innermost If block
551+
Assert.IsFalse(selection.Contains(contexts.ElementAt(1))); // first outer If block
552+
Assert.IsFalse(selection.Contains(contexts.ElementAt(2))); // second outer If block
540553
}
541554

542555
[TestMethod]
@@ -577,9 +590,9 @@ End If
577590
var selection = new Selection(6, 1, 13, 7);
578591

579592
Assert.IsTrue(selection.Contains(token)); // last token in innermost If block
580-
Assert.IsTrue(contexts.ElementAt(0).Contains(selection)); // innermost If block
581-
Assert.IsTrue(contexts.ElementAt(1).Contains(selection)); // first outer If block
582-
Assert.IsFalse(contexts.ElementAt(2).Contains(selection)); // second outer If block
593+
Assert.IsTrue(selection.Contains(contexts.ElementAt(0))); // innermost If block
594+
Assert.IsTrue(selection.Contains(contexts.ElementAt(1))); // first outer If block
595+
Assert.IsFalse(selection.Contains(contexts.ElementAt(2))); // second outer If block
583596
}
584597

585598
[TestMethod]
@@ -620,9 +633,9 @@ End If
620633
var selection = new Selection(15, 1, 19, 7);
621634

622635
Assert.IsFalse(selection.Contains(token)); // last token in innermost If block
623-
Assert.IsFalse(contexts.ElementAt(0).Contains(selection)); // innermost If block
624-
Assert.IsFalse(contexts.ElementAt(1).Contains(selection)); // first outer if block
625-
Assert.IsTrue(contexts.ElementAt(2).Contains(selection)); // second outer If block
636+
Assert.IsFalse(selection.Contains(contexts.ElementAt(0))); // innermost If block
637+
Assert.IsFalse(selection.Contains(contexts.ElementAt(1))); // first outer if block
638+
Assert.IsTrue(selection.Contains(contexts.ElementAt(2))); // second outer If block
626639
}
627640
}
628641
}

0 commit comments

Comments
 (0)