From af7cda6257819fd38cd57ba5300f9ac911bd8954 Mon Sep 17 00:00:00 2001 From: Glenn Sarti Date: Tue, 2 Oct 2018 13:43:57 +0800 Subject: [PATCH] (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 --- package.json | 5 +++ src/features/Folding.ts | 18 ++++++++-- src/settings.ts | 2 ++ test/features/folding.test.ts | 66 +++++++++++++++++++++++++---------- 4 files changed, 71 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 4544bda7d2..0104f56f5a 100644 --- a/package.json +++ b/package.json @@ -478,6 +478,11 @@ "default": true, "description": "Enables syntax based code folding. When disabled, the default indentation based code folding is used." }, + "powershell.codeFolding.showLastLine": { + "type": "boolean", + "default": true, + "description": "Shows the last line of a folded section similar to the default VSCode folding style. When disabled, the entire folded region is hidden." + }, "powershell.codeFormatting.preset": { "type": "string", "enum": [ diff --git a/src/features/Folding.ts b/src/features/Folding.ts index 1e68f3b33b..6b99ab0df2 100644 --- a/src/features/Folding.ts +++ b/src/features/Folding.ts @@ -157,7 +157,10 @@ class LineNumberRange { * Creates a vscode.FoldingRange object based on this object * @returns A Folding Range object for use with the Folding Provider */ - public toFoldingRange(): vscode.FoldingRange { + public toFoldingRange(settings: Settings.ISettings): vscode.FoldingRange { + if (settings.codeFolding && settings.codeFolding.showLastLine) { + return new vscode.FoldingRange(this.startline, this.endline - 1, this.rangeKind); + } return new vscode.FoldingRange(this.startline, this.endline, this.rangeKind); } } @@ -252,15 +255,26 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { return 0; }); + const settings = this.currentSettings(); return foldableRegions // It's possible to have duplicate or overlapping ranges, that is, regions which have the same starting // line number as the previous region. Therefore only emit ranges which have a different starting line // than the previous range. .filter((item, index, src) => index === 0 || item.startline !== src[index - 1].startline) // Convert the internal representation into the VSCode expected type - .map((item) => item.toFoldingRange()); + .map((item) => item.toFoldingRange(settings)); } + /** + * A helper to return the current extension settings. This helper is primarily for use by unit testing so + * that extension settings can be mocked. + * - The settings cannot be set in the constructor as the settings should be evalauted on each folding request + * so that setting changes are immediate, i.e. they do not require an extension reload + * - The method signature for provideFoldingRanges can not be changed as it is explicitly set in the VSCode API, + * therefore the settings can not be passed in the method call, which would be preferred + */ + public currentSettings(): Settings.ISettings { return Settings.load(); } + /** * Given a start and end textmate scope name, find matching grammar tokens * and pair them together. Uses a simple stack to take into account nested regions. diff --git a/src/settings.ts b/src/settings.ts index 42fe34f803..c7a87ffef7 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -31,6 +31,7 @@ export interface IBugReportingSettings { export interface ICodeFoldingSettings { enable?: boolean; + showLastLine?: boolean; } export interface ICodeFormattingSettings { @@ -116,6 +117,7 @@ export function load(): ISettings { const defaultCodeFoldingSettings: ICodeFoldingSettings = { enable: true, + showLastLine: false, }; const defaultCodeFormattingSettings: ICodeFormattingSettings = { diff --git a/test/features/folding.test.ts b/test/features/folding.test.ts index 8f94f96c65..02c15510ad 100644 --- a/test/features/folding.test.ts +++ b/test/features/folding.test.ts @@ -7,6 +7,7 @@ import * as path from "path"; import * as vscode from "vscode"; import { DocumentSelector } from "vscode-languageclient"; import * as folding from "../../src/features/Folding"; +import * as Settings from "../../src/settings"; import { MockLogger } from "../test_utils"; const fixturePath = path.join(__dirname, "..", "..", "..", "test", "fixtures"); @@ -22,6 +23,13 @@ function assertFoldingRegions(result, expected): void { assert.equal(result.length, expected.length); } +// Wrap the FoldingProvider class with our own custom settings for testing +class CustomSettingFoldingProvider extends folding.FoldingProvider { + public customSettings: Settings.ISettings = Settings.load(); + // Overridde the super currentSettings method with our own custom test settings + public currentSettings(): Settings.ISettings { return this.customSettings; } +} + suite("Features", () => { suite("Folding Provider", async () => { @@ -38,21 +46,21 @@ suite("Features", () => { suite("For a single document", async () => { const expectedFoldingRegions = [ - { start: 0, end: 4, kind: 3 }, - { start: 1, end: 3, kind: 1 }, - { start: 10, end: 15, kind: 1 }, - { start: 16, end: 60, kind: null }, - { start: 17, end: 22, kind: 1 }, - { start: 23, end: 26, kind: null }, - { start: 28, end: 31, kind: null }, - { start: 35, end: 37, kind: 1 }, - { start: 39, end: 49, kind: 3 }, - { start: 41, end: 45, kind: 3 }, - { start: 51, end: 53, kind: null }, - { start: 56, end: 59, kind: null }, - { start: 64, end: 66, kind: 1 }, - { start: 67, end: 72, kind: 3 }, - { start: 68, end: 70, kind: 1 }, + { start: 0, end: 3, kind: 3 }, + { start: 1, end: 2, kind: 1 }, + { start: 10, end: 14, kind: 1 }, + { start: 16, end: 59, kind: null }, + { start: 17, end: 21, kind: 1 }, + { start: 23, end: 25, kind: null }, + { start: 28, end: 30, kind: null }, + { start: 35, end: 36, kind: 1 }, + { start: 39, end: 48, kind: 3 }, + { start: 41, end: 44, kind: 3 }, + { start: 51, end: 52, kind: null }, + { start: 56, end: 58, kind: null }, + { start: 64, end: 65, kind: 1 }, + { start: 67, end: 71, kind: 3 }, + { start: 68, end: 69, kind: 1 }, ]; test("Can detect all of the foldable regions in a document with CRLF line endings", async () => { @@ -83,9 +91,31 @@ suite("Features", () => { assertFoldingRegions(result, expectedFoldingRegions); }); + suite("Where showLastLine setting is false", async () => { + const customprovider = (new CustomSettingFoldingProvider(psGrammar)); + customprovider.customSettings.codeFolding.showLastLine = false; + + test("Can detect all foldable regions in a document", async () => { + // Integration test against the test fixture 'folding-lf.ps1' that contains + // all of the different types of folding available + const uri = vscode.Uri.file(path.join(fixturePath, "folding-lf.ps1")); + const document = await vscode.workspace.openTextDocument(uri); + const result = await customprovider.provideFoldingRanges(document, null, null); + + // Incrememnt the end line of the expected regions by one as we will + // be hiding the last line + const expectedLastLineRegions = expectedFoldingRegions.map( (item) => { + item.end++; + return item; + }); + + assertFoldingRegions(result, expectedLastLineRegions); + }); + }); + test("Can detect all of the foldable regions in a document with mismatched regions", async () => { const expectedMismatchedFoldingRegions = [ - { start: 2, end: 4, kind: 3 }, + { start: 2, end: 3, kind: 3 }, ]; // Integration test against the test fixture 'folding-mismatch.ps1' that contains @@ -99,8 +129,8 @@ suite("Features", () => { test("Does not return duplicate or overlapping regions", async () => { const expectedMismatchedFoldingRegions = [ - { start: 1, end: 2, kind: null }, - { start: 2, end: 4, kind: null }, + { start: 1, end: 1, kind: null }, + { start: 2, end: 3, kind: null }, ]; // Integration test against the test fixture 'folding-mismatch.ps1' that contains