Skip to content

Commit bd76cdb

Browse files
committed
(GH-793) Add a folding provider and tests
This commit adds the the code to return folding ranges when the server receives a FoldingRangeRequest. This commit; * Uses a similar method to that implemented in the VS Code extension [1] * Uses the PowerShell tokeniser instead of the AST. This is due to the AST ignoring comment sections. Without the comment parsing folding for #region etc. will not work * Translates the tests in VS Code [1] into equivalent C# tests [1] PowerShell/vscode-powershell#1355
1 parent 392f855 commit bd76cdb

File tree

5 files changed

+635
-0
lines changed

5 files changed

+635
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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 Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
7+
8+
namespace Microsoft.PowerShell.EditorServices.Protocol.LanguageServer
9+
{
10+
public class FoldingRangeRequest
11+
{
12+
/// <summary>
13+
/// A request to provide folding ranges in a document. The request's
14+
/// parameter is of type [FoldingRangeParams](#FoldingRangeParams), the
15+
/// response is of type [FoldingRangeList](#FoldingRangeList) or a Thenable
16+
/// that resolves to such.
17+
/// Ref: https://github.com/Microsoft/vscode-languageserver-node/blob/5350bc2ffe8afb17357c1a66fbdd3845fa05adfd/protocol/src/protocol.foldingRange.ts#L112-L120
18+
/// </summary>
19+
public static readonly
20+
RequestType<FoldingRangeParams, FoldingRange[], object, object> Type =
21+
RequestType<FoldingRangeParams, FoldingRange[], object, object>.Create("textDocument/foldingRange");
22+
}
23+
24+
/// <summary>
25+
/// Parameters for a [FoldingRangeRequest](#FoldingRangeRequest).
26+
/// Ref: https://github.com/Microsoft/vscode-languageserver-node/blob/5350bc2ffe8afb17357c1a66fbdd3845fa05adfd/protocol/src/protocol.foldingRange.ts#L102-L110
27+
/// </summary>
28+
public class FoldingRangeParams
29+
{
30+
/// <summary>
31+
/// The text document
32+
/// </summary>
33+
public TextDocumentIdentifier TextDocument { get; set; }
34+
}
35+
36+
/// <summary>
37+
/// Represents a folding range.
38+
/// Ref: https://github.com/Microsoft/vscode-languageserver-node/blob/5350bc2ffe8afb17357c1a66fbdd3845fa05adfd/protocol/src/protocol.foldingRange.ts#L69-L100
39+
/// </summary>
40+
public class FoldingRange
41+
{
42+
/// <summary>
43+
/// The zero-based line number from where the folded range starts.
44+
/// </summary>
45+
public int StartLine { get; set; }
46+
47+
/// <summary>
48+
/// The zero-based character offset from where the folded range starts. If not defined, defaults to the length of the start line.
49+
/// </summary>
50+
public int StartCharacter { get; set; }
51+
52+
/// <summary>
53+
/// The zero-based line number where the folded range ends.
54+
/// </summary>
55+
public int EndLine { get; set; }
56+
57+
/// <summary>
58+
/// The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line.
59+
/// </summary>
60+
public int EndCharacter { get; set; }
61+
62+
/// <summary>
63+
/// Describes the kind of the folding range such as `comment' or 'region'. The kind
64+
/// is used to categorize folding ranges and used by commands like 'Fold all comments'. See
65+
/// [FoldingRangeKind](#FoldingRangeKind) for an enumeration of standardized kinds.
66+
/// </summary>
67+
public string Kind { get; set; }
68+
}
69+
}

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

+26
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ public void Start()
131131
this.messageHandlers.SetRequestHandler(
132132
DocumentRangeFormattingRequest.Type,
133133
this.HandleDocumentRangeFormattingRequest);
134+
this.messageHandlers.SetRequestHandler(FoldingRangeRequest.Type, this.HandleFoldingRangeRequest);
134135

135136
this.messageHandlers.SetRequestHandler(ShowOnlineHelpRequest.Type, this.HandleShowOnlineHelpRequest);
136137
this.messageHandlers.SetRequestHandler(ShowHelpRequest.Type, this.HandleShowHelpRequest);
@@ -1251,6 +1252,13 @@ await requestContext.SendResult(new TextEdit[1]
12511252
});
12521253
}
12531254

1255+
protected async Task HandleFoldingRangeRequest(
1256+
FoldingRangeParams foldingParams,
1257+
RequestContext<FoldingRange[]> requestContext)
1258+
{
1259+
await requestContext.SendResult(Fold(foldingParams.TextDocument.Uri));
1260+
}
1261+
12541262
protected Task HandleEvaluateRequest(
12551263
DebugAdapterMessages.EvaluateRequestArguments evaluateParams,
12561264
RequestContext<DebugAdapterMessages.EvaluateResponseBody> requestContext)
@@ -1289,6 +1297,24 @@ protected Task HandleEvaluateRequest(
12891297

12901298
#region Event Handlers
12911299

1300+
private FoldingRange[] Fold(
1301+
string documentUri)
1302+
{
1303+
var result = new List<FoldingRange>();
1304+
foreach (FoldingReference fold in TokenOperations.FoldableRegions(
1305+
editorSession.Workspace.GetFile(documentUri).ScriptTokens))
1306+
{
1307+
result.Add(new FoldingRange {
1308+
EndCharacter = fold.EndCharacter,
1309+
EndLine = fold.EndLine,
1310+
Kind = fold.Kind,
1311+
StartCharacter = fold.StartCharacter,
1312+
StartLine = fold.StartLine
1313+
});
1314+
}
1315+
return result.ToArray();
1316+
}
1317+
12921318
private async Task<Tuple<string, Range>> Format(
12931319
string documentUri,
12941320
FormattingOptions options,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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+
8+
namespace Microsoft.PowerShell.EditorServices
9+
{
10+
/// <summary>
11+
/// A class that holds the information for a foldable region of text in a document
12+
/// </summary>
13+
public class FoldingReference: IComparable<FoldingReference>
14+
{
15+
/// <summary>
16+
/// The zero-based line number from where the folded range starts.
17+
/// </summary>
18+
public int StartLine { get; set; }
19+
20+
/// <summary>
21+
/// The zero-based character offset from where the folded range starts. If not defined, defaults to the length of the start line.
22+
/// </summary>
23+
public int StartCharacter { get; set; } = 0;
24+
25+
/// <summary>
26+
/// The zero-based line number where the folded range ends.
27+
/// </summary>
28+
public int EndLine { get; set; }
29+
30+
/// <summary>
31+
/// The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line.
32+
/// </summary>
33+
public int EndCharacter { get; set; } = 0;
34+
35+
/// <summary>
36+
/// Describes the kind of the folding range such as `comment' or 'region'.
37+
/// </summary>
38+
public string Kind { get; set; }
39+
40+
/// <summary>
41+
/// A custom comparable method which can properly sort FoldingReference objects
42+
/// </summary>
43+
public int CompareTo(FoldingReference that) {
44+
// Initially look at the start line
45+
if (this.StartLine < that.StartLine) { return -1; }
46+
if (this.StartLine > that.StartLine) { return 1; }
47+
48+
// They have the same start line so now consider the end line.
49+
// The biggest line range is sorted first
50+
if (this.EndLine > that.EndLine) { return -1; }
51+
if (this.EndLine < that.EndLine) { return 1; }
52+
53+
// They have the same lines, but what about character offsets
54+
if (this.StartCharacter < that.StartCharacter) { return -1; }
55+
if (this.StartCharacter > that.StartCharacter) { return 1; }
56+
if (this.EndCharacter < that.EndCharacter) { return -1; }
57+
if (this.EndCharacter > that.EndCharacter) { return 1; }
58+
59+
// They're the same range, but what about kind
60+
return string.Compare(this.Kind, that.Kind);
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)