Skip to content

Commit 0dfdcb9

Browse files
authored
Merge pull request #3368 from bclothier/next
No user-visible functionality change. Improved extracting `Selection` out of a `ParserRuleContext`; added `Selection`-involving extension methods.
2 parents 4a82792 + 48c91b8 commit 0dfdcb9

File tree

6 files changed

+753
-3
lines changed

6 files changed

+753
-3
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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@
202202
<Compile Include="ComReflection\ComProject.cs" />
203203
<Compile Include="Rewriter\IModuleRewriter.cs" />
204204
<Compile Include="PreProcessing\VBAPreprocessorVisitor.cs" />
205+
<Compile Include="SelectionExtensions.cs" />
205206
<Compile Include="SymbolList.cs" />
206207
<Compile Include="Symbols\AliasDeclaration.cs" />
207208
<Compile Include="Symbols\DeclarationFinderFactory.cs" />
@@ -348,6 +349,7 @@
348349
<Compile Include="Symbols\ConstantDeclaration.cs" />
349350
<Compile Include="ComReflection\XmlPersistableDeclarations.cs" />
350351
<Compile Include="Symbols\UnboundMemberDeclaration.cs" />
352+
<Compile Include="TokenExtensions.cs" />
351353
<Compile Include="VBA\AttributeListener.cs" />
352354
<Compile Include="VBA\AttributeParser.cs" />
353355
<Compile Include="VBA\Attributes.cs" />
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using Antlr4.Runtime;
2+
using Rubberduck.VBEditor;
3+
using System;
4+
5+
namespace Rubberduck.Parsing
6+
{
7+
/// <summary>
8+
/// Provide extensions on selections & contexts/tokens
9+
/// to assist in validating whether a selection contains
10+
/// a given context or a token.
11+
/// </summary>
12+
public static class SelectionExtensions
13+
{
14+
/// <summary>
15+
/// Validates whether a token is contained within a given Selection
16+
/// </summary>
17+
/// <param name="selection">One-based selection, usually from CodePane.Selection</param>
18+
/// <param name="token">An individual token within a module's parse tree</param>
19+
/// <returns>Boolean with true indicating that token is within the selection</returns>
20+
public static bool Contains(this Selection selection, IToken token)
21+
{
22+
return
23+
(((selection.StartLine == token.Line) && (selection.StartColumn - 1) <= token.Column)
24+
|| (selection.StartLine < token.Line))
25+
&& (((selection.EndLine == token.EndLine()) && (selection.EndColumn - 1) >= (token.EndColumn()))
26+
|| (selection.EndLine > token.EndLine()));
27+
}
28+
29+
/// <summary>
30+
/// Validates whether a context is contained within a given Selection
31+
/// </summary>
32+
/// <param name="selection">One-based selection, usually from CodePane.Selection</param>
33+
/// <param name="context">A context which contains several tokens within a module's parse tree</param>
34+
/// <returns>Boolean with true indicating that context is within the selection</returns>
35+
public static bool Contains(this Selection selection, ParserRuleContext context)
36+
{
37+
return
38+
(((selection.StartLine == context.Start.Line) && (selection.StartColumn - 1) <= context.Start.Column)
39+
|| (selection.StartLine < context.Start.Line))
40+
&& (((selection.EndLine == context.Stop.EndLine()) && (selection.EndColumn - 1) >= (context.Stop.EndColumn()))
41+
|| (selection.EndLine > context.Stop.EndLine()));
42+
}
43+
44+
/// <summary>
45+
/// Convenience method for validating that a selection is inside a specified parser rule context.
46+
/// </summary>
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)
51+
{
52+
return context.GetSelection().Contains(selection);
53+
}
54+
}
55+
}

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+
}

0 commit comments

Comments
 (0)