Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b24e7c4

Browse files
committedDec 12, 2018
(GH-813) Use AST for code folding
The AST contains the most correct version of how a script is interpreted. This includes regions of text. Currently the code folder only uses the Tokens which requires the folder to re-implement some of the AST behaviour e.g. matching token pairs for arrays etc. The code folder should be implemented using as much of the AST as possible. This commit; * Moves most of the region detection to use the AST Extents and uses a new FindFoldsASTVisitor. * Modifies the tests and language server to use the new method fold detection class.
1 parent af12135 commit b24e7c4

File tree

6 files changed

+266
-193
lines changed

6 files changed

+266
-193
lines changed
 

‎src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ private async Task HandleGetCommandRequestAsync(
535535
{
536536
PSCommand psCommand = new PSCommand();
537537
if (!string.IsNullOrEmpty(param))
538-
{
538+
{
539539
psCommand.AddCommand("Microsoft.PowerShell.Core\\Get-Command").AddArgument(param);
540540
}
541541
else
@@ -1267,7 +1267,7 @@ protected async Task HandleCodeActionRequest(
12671267
}
12681268
}
12691269

1270-
// Add "show documentation" commands last so they appear at the bottom of the client UI.
1270+
// Add "show documentation" commands last so they appear at the bottom of the client UI.
12711271
// These commands do not require code fixes. Sometimes we get a batch of diagnostics
12721272
// to create commands for. No need to create multiple show doc commands for the same rule.
12731273
var ruleNamesProcessed = new HashSet<string>();
@@ -1382,8 +1382,10 @@ private FoldingRange[] Fold(
13821382
// TODO Should be using dynamic registrations
13831383
if (!this.currentSettings.CodeFolding.Enable) { return null; }
13841384
var result = new List<FoldingRange>();
1385-
foreach (FoldingReference fold in TokenOperations.FoldableRegions(
1386-
editorSession.Workspace.GetFile(documentUri).ScriptTokens,
1385+
ScriptFile script = editorSession.Workspace.GetFile(documentUri);
1386+
foreach (FoldingReference fold in FoldingOperations.FoldableRegions(
1387+
script.ScriptTokens,
1388+
script.ScriptAst,
13871389
this.currentSettings.CodeFolding.ShowLastLine))
13881390
{
13891391
result.Add(new FoldingRange {
@@ -1734,7 +1736,7 @@ await eventSender(
17341736
});
17351737
}
17361738

1737-
// Generate a unique id that is used as a key to look up the associated code action (code fix) when
1739+
// Generate a unique id that is used as a key to look up the associated code action (code fix) when
17381740
// we receive and process the textDocument/codeAction message.
17391741
private static string GetUniqueIdFromDiagnostic(Diagnostic diagnostic)
17401742
{

‎src/PowerShellEditorServices/Language/AstOperations.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,5 +330,19 @@ static public string[] FindDotSourcedIncludes(Ast scriptAst, string psScriptRoot
330330

331331
return dotSourcedVisitor.DotSourcedFiles.ToArray();
332332
}
333+
334+
/// <summary>
335+
/// Finds all foldable regions in a script based on AST
336+
/// </summary>
337+
/// <param name="scriptAst">The abstract syntax tree of the given script</param>
338+
/// <returns>A collection of FoldingReference objects</returns>
339+
static public IEnumerable<FoldingReference> FindFoldsInDocument(Ast scriptAst)
340+
{
341+
FindFoldsVisitor findFoldsVisitor = new FindFoldsVisitor();
342+
scriptAst.Visit(findFoldsVisitor);
343+
344+
return findFoldsVisitor.FoldableRegions;
345+
}
346+
333347
}
334348
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
using System.Collections.Generic;
7+
using System.Management.Automation.Language;
8+
9+
using System;
10+
11+
namespace Microsoft.PowerShell.EditorServices
12+
{
13+
/// <summary>
14+
/// The visitor used to find the all folding regions in an AST
15+
/// </summary>
16+
internal class FindFoldsVisitor : AstVisitor
17+
{
18+
private const string RegionKindNone = null;
19+
20+
public List<FoldingReference> FoldableRegions { get; private set; }
21+
22+
public FindFoldsVisitor()
23+
{
24+
this.FoldableRegions = new List<FoldingReference>();
25+
}
26+
27+
/// <summary>
28+
/// Returns whether an Extent could be used as a valid folding region
29+
/// </summary>
30+
private bool IsValidFoldingExtent(
31+
IScriptExtent extent)
32+
{
33+
if (extent.EndLineNumber == extent.StartLineNumber) { return false; }
34+
return true;
35+
}
36+
37+
/// <summary>
38+
/// Creates an instance of a FoldingReference object from a script extent
39+
/// </summary>
40+
private FoldingReference CreateFoldingReference(
41+
IScriptExtent extent,
42+
string matchKind)
43+
{
44+
// Extents are base 1, but LSP is base 0, so minus 1 off all lines and character positions
45+
return new FoldingReference {
46+
StartLine = extent.StartLineNumber - 1,
47+
StartCharacter = extent.StartColumnNumber - 1,
48+
EndLine = extent.EndLineNumber - 1,
49+
EndCharacter = extent.EndColumnNumber - 1,
50+
Kind = matchKind
51+
};
52+
}
53+
54+
// AST object visitor methods
55+
public override AstVisitAction VisitArrayExpression(ArrayExpressionAst objAst)
56+
{
57+
if (IsValidFoldingExtent(objAst.Extent)) { this.FoldableRegions.Add(CreateFoldingReference(objAst.Extent, RegionKindNone)); }
58+
return AstVisitAction.Continue;
59+
}
60+
61+
public override AstVisitAction VisitHashtable(HashtableAst objAst)
62+
{
63+
if (IsValidFoldingExtent(objAst.Extent)) { this.FoldableRegions.Add(CreateFoldingReference(objAst.Extent, RegionKindNone)); }
64+
return AstVisitAction.Continue;
65+
}
66+
67+
public override AstVisitAction VisitStatementBlock(StatementBlockAst objAst)
68+
{
69+
// These parent visitors will get this AST Object. No need to process it
70+
if (objAst.Parent == null) { return AstVisitAction.Continue; }
71+
if (objAst.Parent is ArrayExpressionAst) { return AstVisitAction.Continue; }
72+
if (IsValidFoldingExtent(objAst.Extent)) { this.FoldableRegions.Add(CreateFoldingReference(objAst.Extent, RegionKindNone)); }
73+
return AstVisitAction.Continue;
74+
}
75+
76+
public override AstVisitAction VisitScriptBlock(ScriptBlockAst objAst)
77+
{
78+
// If the Parent object is null then this represents the entire script. We don't want to fold that
79+
if (objAst.Parent == null) { return AstVisitAction.Continue; }
80+
// The ScriptBlockExpressionAst visitor will get this AST Object. No need to process it
81+
if (objAst.Parent is ScriptBlockExpressionAst) { return AstVisitAction.Continue; }
82+
if (IsValidFoldingExtent(objAst.Extent)) { this.FoldableRegions.Add(CreateFoldingReference(objAst.Extent, RegionKindNone)); }
83+
return AstVisitAction.Continue;
84+
}
85+
86+
public override AstVisitAction VisitScriptBlockExpression(ScriptBlockExpressionAst objAst)
87+
{
88+
if (IsValidFoldingExtent(objAst.Extent)) {
89+
FoldingReference foldRef = CreateFoldingReference(objAst.ScriptBlock.Extent, RegionKindNone);
90+
if (objAst.Parent == null) { return AstVisitAction.Continue; }
91+
if (objAst.Parent is InvokeMemberExpressionAst) {
92+
// This is a bit naive. The ScriptBlockExpressionAst Extent does not include the actual { and }
93+
// characters so the StartCharacter and EndCharacter indexes are out by one. This could be a bug in
94+
// PowerShell Parser. This is just a workaround
95+
foldRef.StartCharacter--;
96+
foldRef.EndCharacter++;
97+
}
98+
this.FoldableRegions.Add(foldRef);
99+
}
100+
return AstVisitAction.Continue;
101+
}
102+
103+
public override AstVisitAction VisitStringConstantExpression(StringConstantExpressionAst objAst)
104+
{
105+
if (IsValidFoldingExtent(objAst.Extent)) { this.FoldableRegions.Add(CreateFoldingReference(objAst.Extent, RegionKindNone)); }
106+
return AstVisitAction.Continue;
107+
}
108+
109+
public override AstVisitAction VisitSubExpression(SubExpressionAst objAst)
110+
{
111+
if (IsValidFoldingExtent(objAst.Extent)) { this.FoldableRegions.Add(CreateFoldingReference(objAst.Extent, RegionKindNone)); }
112+
return AstVisitAction.Continue;
113+
}
114+
115+
public override AstVisitAction VisitVariableExpression(VariableExpressionAst objAst)
116+
{
117+
if (IsValidFoldingExtent(objAst.Extent)) { this.FoldableRegions.Add(CreateFoldingReference(objAst.Extent, RegionKindNone)); }
118+
return AstVisitAction.Continue;
119+
}
120+
}
121+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Management.Automation.Language;
9+
10+
namespace Microsoft.PowerShell.EditorServices
11+
{
12+
/// <summary>
13+
/// Provides common operations for code folding in a script
14+
/// </summary>
15+
internal static class FoldingOperations
16+
{
17+
/// <summary>
18+
/// Extracts all of the unique foldable regions in a script given a script AST and the list tokens
19+
/// used to generate the AST
20+
/// </summary>
21+
internal static FoldingReference[] FoldableRegions(
22+
Token[] tokens,
23+
Ast scriptAst,
24+
bool ShowLastLine)
25+
{
26+
List<FoldingReference> foldableRegions = new List<FoldingReference>();
27+
28+
// Add regions from AST
29+
foldableRegions.AddRange(Microsoft.PowerShell.EditorServices.AstOperations.FindFoldsInDocument(scriptAst));
30+
31+
// Add regions from Tokens
32+
foldableRegions.AddRange(Microsoft.PowerShell.EditorServices.TokenOperations.FoldableRegions(tokens));
33+
34+
// Remove any null entries. Nulls appear if the folding reference is invalid or missing
35+
foldableRegions.RemoveAll(item => item == null);
36+
37+
// Sort the FoldingReferences, starting at the top of the document,
38+
// and ensure that, in the case of multiple ranges starting the same line,
39+
// that the largest range (i.e. most number of lines spanned) is sorted
40+
// first. This is needed to detect duplicate regions. The first in the list
41+
// will be used and subsequent duplicates ignored.
42+
foldableRegions.Sort();
43+
44+
// It's possible to have duplicate or overlapping ranges, that is, regions which have the same starting
45+
// line number as the previous region. Therefore only emit ranges which have a different starting line
46+
// than the previous range.
47+
foldableRegions.RemoveAll( (FoldingReference item) => {
48+
// Note - I'm not happy with searching here, but as the RemoveAll
49+
// doesn't expose the index in the List, we need to calculate it. Fortunately the
50+
// list is sorted at this point, so we can use BinarySearch.
51+
int index = foldableRegions.BinarySearch(item);
52+
if (index == 0) { return false; }
53+
return (item.StartLine == foldableRegions[index - 1].StartLine);
54+
});
55+
56+
// Some editors have different folding UI, sometimes the lastline should be displayed
57+
// If we do want to show the last line, just change the region to be one line less
58+
if (ShowLastLine) {
59+
foldableRegions.ForEach( item => { item.EndLine--; });
60+
}
61+
62+
return foldableRegions.ToArray();
63+
}
64+
}
65+
}

‎src/PowerShellEditorServices/Language/TokenOperations.cs

Lines changed: 51 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
namespace Microsoft.PowerShell.EditorServices
1212
{
13-
1413
/// <summary>
1514
/// Provides common operations for the tokens of a parsed script.
1615
/// </summary>
@@ -20,85 +19,65 @@ internal static class TokenOperations
2019
private const string RegionKindRegion = "region";
2120
private const string RegionKindNone = null;
2221

22+
// These regular expressions are used to match lines which mark the start and end of region comment in a PowerShell
23+
// script. They are based on the defaults in the VS Code Language Configuration at;
24+
// https://github.com/Microsoft/vscode/blob/64186b0a26/extensions/powershell/language-configuration.json#L26-L31
25+
static private readonly Regex s_startRegionTextRegex = new Regex(
26+
@"^\s*#region\b",
27+
RegexOptions.IgnoreCase | RegexOptions.Compiled);
28+
static private readonly Regex s_endRegionTextRegex = new Regex(
29+
@"^\s*#endregion\b",
30+
RegexOptions.IgnoreCase | RegexOptions.Compiled);
31+
2332
/// <summary>
2433
/// Extracts all of the unique foldable regions in a script given the list tokens
2534
/// </summary>
2635
internal static FoldingReference[] FoldableRegions(
27-
Token[] tokens,
28-
bool ShowLastLine)
36+
Token[] tokens)
2937
{
3038
List<FoldingReference> foldableRegions = new List<FoldingReference>();
39+
Stack<Token> tokenCommentRegionStack = new Stack<Token>();
40+
Token blockStartToken = null;
41+
int blockNextLine = -1;
3142

32-
// Find matching braces { -> }
33-
// Find matching hashes @{ -> }
34-
foldableRegions.AddRange(
35-
MatchTokenElements(tokens, new TokenKind[] { TokenKind.LCurly, TokenKind.AtCurly }, TokenKind.RCurly, RegionKindNone)
36-
);
37-
38-
// Find matching parentheses ( -> )
39-
// Find matching array literals @( -> )
40-
// Find matching subexpressions $( -> )
41-
foldableRegions.AddRange(
42-
MatchTokenElements(tokens, new TokenKind[] { TokenKind.LParen, TokenKind.AtParen, TokenKind.DollarParen }, TokenKind.RParen, RegionKindNone)
43-
);
44-
45-
// Find contiguous here strings @' -> '@
46-
foldableRegions.AddRange(
47-
MatchTokenElement(tokens, TokenKind.HereStringLiteral, RegionKindNone)
48-
);
49-
50-
// Find unopinionated variable names ${ \n \n }
51-
foldableRegions.AddRange(
52-
MatchTokenElement(tokens, TokenKind.Variable, RegionKindNone)
53-
);
54-
55-
// Find contiguous here strings @" -> "@
56-
foldableRegions.AddRange(
57-
MatchTokenElement(tokens, TokenKind.HereStringExpandable, RegionKindNone)
58-
);
59-
60-
// Find matching comment regions #region -> #endregion
61-
foldableRegions.AddRange(
62-
MatchCustomCommentRegionTokenElements(tokens, RegionKindRegion)
63-
);
64-
65-
// Find blocks of line comments # comment1\n# comment2\n...
66-
foldableRegions.AddRange(
67-
MatchBlockCommentTokenElement(tokens, RegionKindComment)
68-
);
69-
70-
// Find comments regions <# -> #>
71-
foldableRegions.AddRange(
72-
MatchTokenElement(tokens, TokenKind.Comment, RegionKindComment)
73-
);
74-
75-
// Remove any null entries. Nulls appear if the folding reference is invalid
76-
// or missing
77-
foldableRegions.RemoveAll(item => item == null);
78-
79-
// Sort the FoldingReferences, starting at the top of the document,
80-
// and ensure that, in the case of multiple ranges starting the same line,
81-
// that the largest range (i.e. most number of lines spanned) is sorted
82-
// first. This is needed to detect duplicate regions. The first in the list
83-
// will be used and subsequent duplicates ignored.
84-
foldableRegions.Sort();
85-
86-
// It's possible to have duplicate or overlapping ranges, that is, regions which have the same starting
87-
// line number as the previous region. Therefore only emit ranges which have a different starting line
88-
// than the previous range.
89-
foldableRegions.RemoveAll( (FoldingReference item) => {
90-
// Note - I'm not happy with searching here, but as the RemoveAll
91-
// doesn't expose the index in the List, we need to calculate it. Fortunately the
92-
// list is sorted at this point, so we can use BinarySearch.
93-
int index = foldableRegions.BinarySearch(item);
94-
if (index == 0) { return false; }
95-
return (item.StartLine == foldableRegions[index - 1].StartLine);
96-
});
43+
for (int index = 0; index < tokens.Length; index++)
44+
{
45+
Token token = tokens[index];
46+
if (token.Kind == TokenKind.Comment) {
47+
// Processing for comment regions <# -> #>
48+
if ((token.Kind == TokenKind.Comment) && (token.Extent.StartLineNumber != token.Extent.EndLineNumber)) {
49+
foldableRegions.Add(CreateFoldingReference(token, token, RegionKindComment));
50+
}
9751

98-
// Some editors have different folding UI, sometimes the lastline should be displayed
99-
// If we do want to show the last line, just change the region to be one line less
100-
if (ShowLastLine) {
101-
foldableRegions.ForEach( item => { item.EndLine--; });
52+
if (IsBlockComment(index, tokens)) {
53+
// Regex's are very expensive. Use them sparingly.
54+
bool isStartRegion = s_startRegionTextRegex.IsMatch(token.Text);
55+
bool isEndRegion = s_endRegionTextRegex.IsMatch(token.Text);
56+
// Processing for # region -> # endregion
57+
if (isStartRegion) {
58+
tokenCommentRegionStack.Push(token);
59+
}
60+
if ((tokenCommentRegionStack.Count > 0) && isEndRegion) {
61+
foldableRegions.Add(CreateFoldingReference(tokenCommentRegionStack.Pop(), token, RegionKindRegion));
62+
}
63+
// If it's neither a start or end regsion then it could be block line comment
64+
// Processing for blocks of line comments # comment1\n# comment2\n...
65+
if (!(isStartRegion || isEndRegion)) {
66+
int thisLine = token.Extent.StartLineNumber - 1;
67+
if ((blockStartToken != null) && (thisLine != blockNextLine)) {
68+
foldableRegions.Add(CreateFoldingReference(blockStartToken, blockNextLine - 1, RegionKindComment));
69+
blockStartToken = token;
70+
}
71+
if (blockStartToken == null) { blockStartToken = token; }
72+
blockNextLine = thisLine + 1;
73+
}
74+
}
75+
}
76+
}
77+
// If we exit the token array and we're still processing comment lines, then the
78+
// comment block simply ends at the end of document
79+
if (blockStartToken != null) {
80+
foldableRegions.Add(CreateFoldingReference(blockStartToken, blockNextLine - 1, RegionKindComment));
10281
}
10382

10483
return foldableRegions.ToArray();
@@ -144,47 +123,6 @@ static private FoldingReference CreateFoldingReference(
144123
};
145124
}
146125

147-
/// <summary>
148-
/// Given an array of tokens, find matching regions which start (array of tokens) and end with a different TokenKind
149-
/// </summary>
150-
static private List<FoldingReference> MatchTokenElements(
151-
Token[] tokens,
152-
TokenKind[] startTokenKind,
153-
TokenKind endTokenKind,
154-
string matchKind)
155-
{
156-
List<FoldingReference> result = new List<FoldingReference>();
157-
Stack<Token> tokenStack = new Stack<Token>();
158-
foreach (Token token in tokens)
159-
{
160-
if (Array.IndexOf(startTokenKind, token.Kind) != -1) {
161-
tokenStack.Push(token);
162-
}
163-
if ((tokenStack.Count > 0) && (token.Kind == endTokenKind)) {
164-
result.Add(CreateFoldingReference(tokenStack.Pop(), token, matchKind));
165-
}
166-
}
167-
return result;
168-
}
169-
170-
/// <summary>
171-
/// Given an array of token, finds a specific token
172-
/// </summary>
173-
static private List<FoldingReference> MatchTokenElement(
174-
Token[] tokens,
175-
TokenKind tokenKind,
176-
string matchKind)
177-
{
178-
List<FoldingReference> result = new List<FoldingReference>();
179-
foreach (Token token in tokens)
180-
{
181-
if ((token.Kind == tokenKind) && (token.Extent.StartLineNumber != token.Extent.EndLineNumber)) {
182-
result.Add(CreateFoldingReference(token, token, matchKind));
183-
}
184-
}
185-
return result;
186-
}
187-
188126
/// <summary>
189127
/// Returns true if a Token is a block comment;
190128
/// - Must be a TokenKind.comment
@@ -199,79 +137,5 @@ static private bool IsBlockComment(int index, Token[] tokens) {
199137
if (tokens[index - 1].Kind != TokenKind.NewLine) { return false; }
200138
return thisToken.Text.StartsWith("#");
201139
}
202-
203-
// This regular expressions is used to detect a line comment (as opposed to an inline comment), that is not a region
204-
// block directive i.e.
205-
// - No text between the beginning of the line and `#`
206-
// - Comment does start with region
207-
// - Comment does start with endregion
208-
static private readonly Regex s_nonRegionLineCommentRegex = new Regex(
209-
@"\s*#(?!region\b|endregion\b)",
210-
RegexOptions.IgnoreCase | RegexOptions.Compiled);
211-
212-
/// <summary>
213-
/// Finding blocks of comment tokens is more complicated as the newline characters are not
214-
/// classed as comments. To workaround this we search for valid block comments (See IsBlockCmment)
215-
/// and then determine contiguous line numbers from there
216-
/// </summary>
217-
static private List<FoldingReference> MatchBlockCommentTokenElement(
218-
Token[] tokens,
219-
string matchKind)
220-
{
221-
List<FoldingReference> result = new List<FoldingReference>();
222-
Token startToken = null;
223-
int nextLine = -1;
224-
for (int index = 0; index < tokens.Length; index++)
225-
{
226-
Token thisToken = tokens[index];
227-
if (IsBlockComment(index, tokens) && s_nonRegionLineCommentRegex.IsMatch(thisToken.Text)) {
228-
int thisLine = thisToken.Extent.StartLineNumber - 1;
229-
if ((startToken != null) && (thisLine != nextLine)) {
230-
result.Add(CreateFoldingReference(startToken, nextLine - 1, matchKind));
231-
startToken = thisToken;
232-
}
233-
if (startToken == null) { startToken = thisToken; }
234-
nextLine = thisLine + 1;
235-
}
236-
}
237-
// If we exit the token array and we're still processing comment lines, then the
238-
// comment block simply ends at the end of document
239-
if (startToken != null) {
240-
result.Add(CreateFoldingReference(startToken, nextLine - 1, matchKind));
241-
}
242-
return result;
243-
}
244-
245-
/// <summary>
246-
/// Given a list of tokens, find the tokens that are comments and
247-
/// the comment text is either `# region` or `# endregion`, and then use a stack to determine
248-
/// the ranges they span
249-
/// </summary>
250-
static private List<FoldingReference> MatchCustomCommentRegionTokenElements(
251-
Token[] tokens,
252-
string matchKind)
253-
{
254-
// These regular expressions are used to match lines which mark the start and end of region comment in a PowerShell
255-
// script. They are based on the defaults in the VS Code Language Configuration at;
256-
// https://github.com/Microsoft/vscode/blob/64186b0a26/extensions/powershell/language-configuration.json#L26-L31
257-
string startRegionText = @"^\s*#region\b";
258-
string endRegionText = @"^\s*#endregion\b";
259-
260-
List<FoldingReference> result = new List<FoldingReference>();
261-
Stack<Token> tokenStack = new Stack<Token>();
262-
for (int index = 0; index < tokens.Length; index++)
263-
{
264-
if (IsBlockComment(index, tokens)) {
265-
Token token = tokens[index];
266-
if (Regex.IsMatch(token.Text, startRegionText, RegexOptions.IgnoreCase)) {
267-
tokenStack.Push(token);
268-
}
269-
if ((tokenStack.Count > 0) && (Regex.IsMatch(token.Text, endRegionText, RegexOptions.IgnoreCase))) {
270-
result.Add(CreateFoldingReference(tokenStack.Pop(), token, matchKind));
271-
}
272-
}
273-
}
274-
return result;
275-
}
276140
}
277141
}

‎test/PowerShellEditorServices.Test/Language/TokenOperationsTests.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace Microsoft.PowerShell.EditorServices.Test.Language
1010
{
1111
public class TokenOperationsTests
1212
{
13+
1314
/// <summary>
1415
/// Helper method to create a stub script file and then call FoldableRegions
1516
/// </summary>
@@ -19,7 +20,11 @@ private FoldingReference[] GetRegions(string text, bool showLastLine = true) {
1920
"clienttestfile",
2021
text,
2122
Version.Parse("5.0"));
22-
return Microsoft.PowerShell.EditorServices.TokenOperations.FoldableRegions(scriptFile.ScriptTokens, showLastLine);
23+
24+
return Microsoft.PowerShell.EditorServices.FoldingOperations.FoldableRegions(
25+
scriptFile.ScriptTokens,
26+
scriptFile.ScriptAst,
27+
showLastLine);
2328
}
2429

2530
/// <summary>
@@ -147,6 +152,8 @@ private void AssertFoldingReferenceArrays(
147152
FoldingReference[] expected,
148153
FoldingReference[] actual)
149154
{
155+
int minValue = expected.Length;
156+
if (minValue < actual.Length) { minValue = actual.Length; }
150157
for (int index = 0; index < expected.Length; index++)
151158
{
152159
Assert.Equal(expected[index], actual[index]);

0 commit comments

Comments
 (0)
Please sign in to comment.