Skip to content

Commit 81b3cee

Browse files
glennsartiTylerLeonhardt
authored andcommitted
(GH-824)(GH-812) Improve code folding speed (#825)
* (maint) Add tests for code folding for PowerShell classes Previously there were no tests for PowerShell classes. This commit adds a simple test for this scenario to ensure future changes do not break folding. * (GH-824) Refactor the FoldingReference arrays and lists into it's own class Previously the folding provider created many intermediate arrays and lists and required post-processing. This commit changes the behaviour to use an accumlator patter with an extended Dictionary class. This new class adds a `SafeAdd` method to add FoldingRanges, which then has the logic to determine if the range should indeed be added, for example, passing nulls or pre-existing larger ranges. By passing around this list using ByReference we can avoid creating many objects which are just then thrown away. This commit also moves the ShowLastLine code from the FoldingProvider into the Language Server. This reduces the number of array enumerations to one. * (GH-824) Refactor Token Folding Operations Previously each token type detection was separated into discrete blocks to make reading the code easier. However this meant there were many enumerations of the Tokens array as well as passing around intermediate arrays/lists. This commit takes the individual methods and overlaps them to reduce the number of enumerations and regular expression matching to a minimum. Note that there are considerable code comments here due to the code now being more complex on initial review. * (GH-812) Update folder for DSC style scripts Previously the code folding was not tested against DSC configuration scripts. This commit adds tests for a sample DSC script to ensure the folding occurs at the correct places * (GH-824) More strict block comment folding detection Previously the folder would search for the region markers without case sensitivity. This commit modifies the regular expressions to be more strict on the what is a region marker and adds a negative test to ensure that regions that are not cased correctly are not folded .
1 parent e9445d9 commit 81b3cee

File tree

4 files changed

+287
-243
lines changed

4 files changed

+287
-243
lines changed

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

+8-7
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>();
@@ -1390,14 +1390,15 @@ private FoldingRange[] Fold(string documentUri)
13901390
if (!editorSession.Workspace.TryGetFile(documentUri, out scriptFile)) { return null; }
13911391

13921392
var result = new List<FoldingRange>();
1393-
FoldingReference[] foldableRegions =
1394-
TokenOperations.FoldableRegions(scriptFile.ScriptTokens, this.currentSettings.CodeFolding.ShowLastLine);
13951393

1396-
foreach (FoldingReference fold in foldableRegions)
1394+
// If we're showing the last line, decrement the Endline of all regions by one.
1395+
int endLineOffset = this.currentSettings.CodeFolding.ShowLastLine ? -1 : 0;
1396+
1397+
foreach (FoldingReference fold in TokenOperations.FoldableReferences(scriptFile.ScriptTokens).References)
13971398
{
13981399
result.Add(new FoldingRange {
13991400
EndCharacter = fold.EndCharacter,
1400-
EndLine = fold.EndLine,
1401+
EndLine = fold.EndLine + endLineOffset,
14011402
Kind = fold.Kind,
14021403
StartCharacter = fold.StartCharacter,
14031404
StartLine = fold.StartLine
@@ -1744,7 +1745,7 @@ await eventSender(
17441745
});
17451746
}
17461747

1747-
// Generate a unique id that is used as a key to look up the associated code action (code fix) when
1748+
// Generate a unique id that is used as a key to look up the associated code action (code fix) when
17481749
// we receive and process the textDocument/codeAction message.
17491750
private static string GetUniqueIdFromDiagnostic(Diagnostic diagnostic)
17501751
{

src/PowerShellEditorServices/Language/FoldingReference.cs

+49
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//
55

66
using System;
7+
using System.Collections.Generic;
78

89
namespace Microsoft.PowerShell.EditorServices
910
{
@@ -60,4 +61,52 @@ public int CompareTo(FoldingReference that) {
6061
return string.Compare(this.Kind, that.Kind);
6162
}
6263
}
64+
65+
/// <summary>
66+
/// A class that holds a list of FoldingReferences and ensures that when adding a reference that the
67+
/// folding rules are obeyed, e.g. Only one fold per start line
68+
/// </summary>
69+
public class FoldingReferenceList
70+
{
71+
private readonly Dictionary<int, FoldingReference> references = new Dictionary<int, FoldingReference>();
72+
73+
/// <summary>
74+
/// Return all references in the list
75+
/// </summary>
76+
public IEnumerable<FoldingReference> References
77+
{
78+
get
79+
{
80+
return references.Values;
81+
}
82+
}
83+
84+
/// <summary>
85+
/// Adds a FoldingReference to the list and enforces ordering rules e.g. Only one fold per start line
86+
/// </summary>
87+
public void SafeAdd(FoldingReference item)
88+
{
89+
if (item == null) { return; }
90+
91+
// Only add the item if it hasn't been seen before or it's the largest range
92+
if (references.TryGetValue(item.StartLine, out FoldingReference currentItem))
93+
{
94+
if (currentItem.CompareTo(item) == 1) { references[item.StartLine] = item; }
95+
}
96+
else
97+
{
98+
references[item.StartLine] = item;
99+
}
100+
}
101+
102+
/// <summary>
103+
/// Helper method to easily convert the Dictionary Values into an array
104+
/// </summary>
105+
public FoldingReference[] ToArray()
106+
{
107+
var result = new FoldingReference[references.Count];
108+
references.Values.CopyTo(result, 0);
109+
return result;
110+
}
111+
}
63112
}

0 commit comments

Comments
 (0)