Skip to content

Commit af7cda6

Browse files
committed
(GH-1514) Show the last line of folding regions as per default VSCode
Previously the code folding provider would hide the last line of the region, however the default VS Code code folding behaviour is to instead hide from the beginning to end minus one, lines. This commit; * Adds a configuration parameter to show/hide the last line in a region, defaulting to Show the last line * Modifies conversion method for the internal region class to FoldingRange object to show/hide the last line based on the extension settings * Modifies the tests for the default value and adds tests for the show and hide scenarios This code is based on a solution created by; ens-rpitcher <[email protected]>
1 parent 78f2bb3 commit af7cda6

File tree

4 files changed

+71
-20
lines changed

4 files changed

+71
-20
lines changed

package.json

+5
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,11 @@
478478
"default": true,
479479
"description": "Enables syntax based code folding. When disabled, the default indentation based code folding is used."
480480
},
481+
"powershell.codeFolding.showLastLine": {
482+
"type": "boolean",
483+
"default": true,
484+
"description": "Shows the last line of a folded section similar to the default VSCode folding style. When disabled, the entire folded region is hidden."
485+
},
481486
"powershell.codeFormatting.preset": {
482487
"type": "string",
483488
"enum": [

src/features/Folding.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,10 @@ class LineNumberRange {
157157
* Creates a vscode.FoldingRange object based on this object
158158
* @returns A Folding Range object for use with the Folding Provider
159159
*/
160-
public toFoldingRange(): vscode.FoldingRange {
160+
public toFoldingRange(settings: Settings.ISettings): vscode.FoldingRange {
161+
if (settings.codeFolding && settings.codeFolding.showLastLine) {
162+
return new vscode.FoldingRange(this.startline, this.endline - 1, this.rangeKind);
163+
}
161164
return new vscode.FoldingRange(this.startline, this.endline, this.rangeKind);
162165
}
163166
}
@@ -252,15 +255,26 @@ export class FoldingProvider implements vscode.FoldingRangeProvider {
252255
return 0;
253256
});
254257

258+
const settings = this.currentSettings();
255259
return foldableRegions
256260
// It's possible to have duplicate or overlapping ranges, that is, regions which have the same starting
257261
// line number as the previous region. Therefore only emit ranges which have a different starting line
258262
// than the previous range.
259263
.filter((item, index, src) => index === 0 || item.startline !== src[index - 1].startline)
260264
// Convert the internal representation into the VSCode expected type
261-
.map((item) => item.toFoldingRange());
265+
.map((item) => item.toFoldingRange(settings));
262266
}
263267

268+
/**
269+
* A helper to return the current extension settings. This helper is primarily for use by unit testing so
270+
* that extension settings can be mocked.
271+
* - The settings cannot be set in the constructor as the settings should be evalauted on each folding request
272+
* so that setting changes are immediate, i.e. they do not require an extension reload
273+
* - The method signature for provideFoldingRanges can not be changed as it is explicitly set in the VSCode API,
274+
* therefore the settings can not be passed in the method call, which would be preferred
275+
*/
276+
public currentSettings(): Settings.ISettings { return Settings.load(); }
277+
264278
/**
265279
* Given a start and end textmate scope name, find matching grammar tokens
266280
* and pair them together. Uses a simple stack to take into account nested regions.

src/settings.ts

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export interface IBugReportingSettings {
3131

3232
export interface ICodeFoldingSettings {
3333
enable?: boolean;
34+
showLastLine?: boolean;
3435
}
3536

3637
export interface ICodeFormattingSettings {
@@ -116,6 +117,7 @@ export function load(): ISettings {
116117

117118
const defaultCodeFoldingSettings: ICodeFoldingSettings = {
118119
enable: true,
120+
showLastLine: false,
119121
};
120122

121123
const defaultCodeFormattingSettings: ICodeFormattingSettings = {

test/features/folding.test.ts

+48-18
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as path from "path";
77
import * as vscode from "vscode";
88
import { DocumentSelector } from "vscode-languageclient";
99
import * as folding from "../../src/features/Folding";
10+
import * as Settings from "../../src/settings";
1011
import { MockLogger } from "../test_utils";
1112

1213
const fixturePath = path.join(__dirname, "..", "..", "..", "test", "fixtures");
@@ -22,6 +23,13 @@ function assertFoldingRegions(result, expected): void {
2223
assert.equal(result.length, expected.length);
2324
}
2425

26+
// Wrap the FoldingProvider class with our own custom settings for testing
27+
class CustomSettingFoldingProvider extends folding.FoldingProvider {
28+
public customSettings: Settings.ISettings = Settings.load();
29+
// Overridde the super currentSettings method with our own custom test settings
30+
public currentSettings(): Settings.ISettings { return this.customSettings; }
31+
}
32+
2533
suite("Features", () => {
2634

2735
suite("Folding Provider", async () => {
@@ -38,21 +46,21 @@ suite("Features", () => {
3846

3947
suite("For a single document", async () => {
4048
const expectedFoldingRegions = [
41-
{ start: 0, end: 4, kind: 3 },
42-
{ start: 1, end: 3, kind: 1 },
43-
{ start: 10, end: 15, kind: 1 },
44-
{ start: 16, end: 60, kind: null },
45-
{ start: 17, end: 22, kind: 1 },
46-
{ start: 23, end: 26, kind: null },
47-
{ start: 28, end: 31, kind: null },
48-
{ start: 35, end: 37, kind: 1 },
49-
{ start: 39, end: 49, kind: 3 },
50-
{ start: 41, end: 45, kind: 3 },
51-
{ start: 51, end: 53, kind: null },
52-
{ start: 56, end: 59, kind: null },
53-
{ start: 64, end: 66, kind: 1 },
54-
{ start: 67, end: 72, kind: 3 },
55-
{ start: 68, end: 70, kind: 1 },
49+
{ start: 0, end: 3, kind: 3 },
50+
{ start: 1, end: 2, kind: 1 },
51+
{ start: 10, end: 14, kind: 1 },
52+
{ start: 16, end: 59, kind: null },
53+
{ start: 17, end: 21, kind: 1 },
54+
{ start: 23, end: 25, kind: null },
55+
{ start: 28, end: 30, kind: null },
56+
{ start: 35, end: 36, kind: 1 },
57+
{ start: 39, end: 48, kind: 3 },
58+
{ start: 41, end: 44, kind: 3 },
59+
{ start: 51, end: 52, kind: null },
60+
{ start: 56, end: 58, kind: null },
61+
{ start: 64, end: 65, kind: 1 },
62+
{ start: 67, end: 71, kind: 3 },
63+
{ start: 68, end: 69, kind: 1 },
5664
];
5765

5866
test("Can detect all of the foldable regions in a document with CRLF line endings", async () => {
@@ -83,9 +91,31 @@ suite("Features", () => {
8391
assertFoldingRegions(result, expectedFoldingRegions);
8492
});
8593

94+
suite("Where showLastLine setting is false", async () => {
95+
const customprovider = (new CustomSettingFoldingProvider(psGrammar));
96+
customprovider.customSettings.codeFolding.showLastLine = false;
97+
98+
test("Can detect all foldable regions in a document", async () => {
99+
// Integration test against the test fixture 'folding-lf.ps1' that contains
100+
// all of the different types of folding available
101+
const uri = vscode.Uri.file(path.join(fixturePath, "folding-lf.ps1"));
102+
const document = await vscode.workspace.openTextDocument(uri);
103+
const result = await customprovider.provideFoldingRanges(document, null, null);
104+
105+
// Incrememnt the end line of the expected regions by one as we will
106+
// be hiding the last line
107+
const expectedLastLineRegions = expectedFoldingRegions.map( (item) => {
108+
item.end++;
109+
return item;
110+
});
111+
112+
assertFoldingRegions(result, expectedLastLineRegions);
113+
});
114+
});
115+
86116
test("Can detect all of the foldable regions in a document with mismatched regions", async () => {
87117
const expectedMismatchedFoldingRegions = [
88-
{ start: 2, end: 4, kind: 3 },
118+
{ start: 2, end: 3, kind: 3 },
89119
];
90120

91121
// Integration test against the test fixture 'folding-mismatch.ps1' that contains
@@ -99,8 +129,8 @@ suite("Features", () => {
99129

100130
test("Does not return duplicate or overlapping regions", async () => {
101131
const expectedMismatchedFoldingRegions = [
102-
{ start: 1, end: 2, kind: null },
103-
{ start: 2, end: 4, kind: null },
132+
{ start: 1, end: 1, kind: null },
133+
{ start: 2, end: 3, kind: null },
104134
];
105135

106136
// Integration test against the test fixture 'folding-mismatch.ps1' that contains

0 commit comments

Comments
 (0)