Path: blob/main/src/vs/editor/contrib/folding/test/browser/foldingModel.test.ts
4780 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/4import assert from 'assert';5import { escapeRegExpCharacters } from '../../../../../base/common/strings.js';6import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';7import { EditOperation } from '../../../../common/core/editOperation.js';8import { Position } from '../../../../common/core/position.js';9import { Range } from '../../../../common/core/range.js';10import { Selection } from '../../../../common/core/selection.js';11import { IModelDecorationsChangeAccessor, ITextModel, TrackedRangeStickiness } from '../../../../common/model.js';12import { ModelDecorationOptions } from '../../../../common/model/textModel.js';13import { toSelectedLines } from '../../browser/folding.js';14import { FoldingModel, getNextFoldLine, getParentFoldLine, getPreviousFoldLine, setCollapseStateAtLevel, setCollapseStateForMatchingLines, setCollapseStateForRest, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateUp } from '../../browser/foldingModel.js';15import { FoldingRegion } from '../../browser/foldingRanges.js';16import { computeRanges } from '../../browser/indentRangeProvider.js';17import { createTextModel } from '../../../../test/common/testTextModel.js';181920interface ExpectedRegion {21startLineNumber: number;22endLineNumber: number;23isCollapsed: boolean;24}2526interface ExpectedDecoration {27line: number;28type: 'hidden' | 'collapsed' | 'expanded';29}3031export class TestDecorationProvider {3233private static readonly collapsedDecoration = ModelDecorationOptions.register({34description: 'test',35stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,36linesDecorationsClassName: 'folding'37});3839private static readonly expandedDecoration = ModelDecorationOptions.register({40description: 'test',41stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,42linesDecorationsClassName: 'folding'43});4445private static readonly hiddenDecoration = ModelDecorationOptions.register({46description: 'test',47stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,48linesDecorationsClassName: 'folding'49});5051constructor(private model: ITextModel) {52}5354getDecorationOption(isCollapsed: boolean, isHidden: boolean): ModelDecorationOptions {55if (isHidden) {56return TestDecorationProvider.hiddenDecoration;57}58if (isCollapsed) {59return TestDecorationProvider.collapsedDecoration;60}61return TestDecorationProvider.expandedDecoration;62}6364changeDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): (T | null) {65return this.model.changeDecorations(callback);66}6768removeDecorations(decorationIds: string[]): void {69this.model.changeDecorations((changeAccessor) => {70changeAccessor.deltaDecorations(decorationIds, []);71});72}7374getDecorations(): ExpectedDecoration[] {75const decorations = this.model.getAllDecorations();76const res: ExpectedDecoration[] = [];77for (const decoration of decorations) {78if (decoration.options === TestDecorationProvider.hiddenDecoration) {79res.push({ line: decoration.range.startLineNumber, type: 'hidden' });80} else if (decoration.options === TestDecorationProvider.collapsedDecoration) {81res.push({ line: decoration.range.startLineNumber, type: 'collapsed' });82} else if (decoration.options === TestDecorationProvider.expandedDecoration) {83res.push({ line: decoration.range.startLineNumber, type: 'expanded' });84}85}86return res;87}88}8990suite('Folding Model', () => {91ensureNoDisposablesAreLeakedInTestSuite();92function r(startLineNumber: number, endLineNumber: number, isCollapsed: boolean = false): ExpectedRegion {93return { startLineNumber, endLineNumber, isCollapsed };94}9596function d(line: number, type: 'hidden' | 'collapsed' | 'expanded'): ExpectedDecoration {97return { line, type };98}99100function assertRegion(actual: FoldingRegion | null, expected: ExpectedRegion | null, message?: string) {101assert.strictEqual(!!actual, !!expected, message);102if (actual && expected) {103assert.strictEqual(actual.startLineNumber, expected.startLineNumber, message);104assert.strictEqual(actual.endLineNumber, expected.endLineNumber, message);105assert.strictEqual(actual.isCollapsed, expected.isCollapsed, message);106}107}108109function assertFoldedRanges(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) {110const actualRanges: ExpectedRegion[] = [];111const actual = foldingModel.regions;112for (let i = 0; i < actual.length; i++) {113if (actual.isCollapsed(i)) {114actualRanges.push(r(actual.getStartLineNumber(i), actual.getEndLineNumber(i)));115}116}117assert.deepStrictEqual(actualRanges, expectedRegions, message);118}119120function assertRanges(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) {121const actualRanges: ExpectedRegion[] = [];122const actual = foldingModel.regions;123for (let i = 0; i < actual.length; i++) {124actualRanges.push(r(actual.getStartLineNumber(i), actual.getEndLineNumber(i), actual.isCollapsed(i)));125}126assert.deepStrictEqual(actualRanges, expectedRegions, message);127}128129function assertDecorations(foldingModel: FoldingModel, expectedDecoration: ExpectedDecoration[], message?: string) {130const decorationProvider = foldingModel.decorationProvider as TestDecorationProvider;131assert.deepStrictEqual(decorationProvider.getDecorations(), expectedDecoration, message);132}133134function assertRegions(actual: FoldingRegion[], expectedRegions: ExpectedRegion[], message?: string) {135assert.deepStrictEqual(actual.map(r => ({ startLineNumber: r.startLineNumber, endLineNumber: r.endLineNumber, isCollapsed: r.isCollapsed })), expectedRegions, message);136}137138test('getRegionAtLine', () => {139const lines = [140/* 1*/ '/**',141/* 2*/ ' * Comment',142/* 3*/ ' */',143/* 4*/ 'class A {',144/* 5*/ ' void foo() {',145/* 6*/ ' // comment {',146/* 7*/ ' }',147/* 8*/ '}'];148149const textModel = createTextModel(lines.join('\n'));150try {151const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));152153const ranges = computeRanges(textModel, false, undefined);154foldingModel.update(ranges);155156const r1 = r(1, 3, false);157const r2 = r(4, 7, false);158const r3 = r(5, 6, false);159160assertRanges(foldingModel, [r1, r2, r3]);161162assertRegion(foldingModel.getRegionAtLine(1), r1, '1');163assertRegion(foldingModel.getRegionAtLine(2), r1, '2');164assertRegion(foldingModel.getRegionAtLine(3), r1, '3');165assertRegion(foldingModel.getRegionAtLine(4), r2, '4');166assertRegion(foldingModel.getRegionAtLine(5), r3, '5');167assertRegion(foldingModel.getRegionAtLine(6), r3, '5');168assertRegion(foldingModel.getRegionAtLine(7), r2, '6');169assertRegion(foldingModel.getRegionAtLine(8), null, '7');170} finally {171textModel.dispose();172}173174175});176177test('collapse', () => {178const lines = [179/* 1*/ '/**',180/* 2*/ ' * Comment',181/* 3*/ ' */',182/* 4*/ 'class A {',183/* 5*/ ' void foo() {',184/* 6*/ ' // comment {',185/* 7*/ ' }',186/* 8*/ '}'];187188const textModel = createTextModel(lines.join('\n'));189try {190const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));191192const ranges = computeRanges(textModel, false, undefined);193foldingModel.update(ranges);194195const r1 = r(1, 3, false);196const r2 = r(4, 7, false);197const r3 = r(5, 6, false);198199assertRanges(foldingModel, [r1, r2, r3]);200201foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)!]);202foldingModel.update(ranges);203204assertRanges(foldingModel, [r(1, 3, true), r2, r3]);205206foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(5)!]);207foldingModel.update(ranges);208209assertRanges(foldingModel, [r(1, 3, true), r2, r(5, 6, true)]);210211foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(7)!]);212foldingModel.update(ranges);213214assertRanges(foldingModel, [r(1, 3, true), r(4, 7, true), r(5, 6, true)]);215216textModel.dispose();217} finally {218textModel.dispose();219}220221});222223test('update', () => {224const lines = [225/* 1*/ '/**',226/* 2*/ ' * Comment',227/* 3*/ ' */',228/* 4*/ 'class A {',229/* 5*/ ' void foo() {',230/* 6*/ ' // comment {',231/* 7*/ ' }',232/* 8*/ '}'];233234const textModel = createTextModel(lines.join('\n'));235try {236const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));237238const ranges = computeRanges(textModel, false, undefined);239foldingModel.update(ranges);240241const r1 = r(1, 3, false);242const r2 = r(4, 7, false);243const r3 = r(5, 6, false);244245assertRanges(foldingModel, [r1, r2, r3]);246foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(2)!, foldingModel.getRegionAtLine(5)!]);247248textModel.applyEdits([EditOperation.insert(new Position(4, 1), '//hello\n')]);249250foldingModel.update(computeRanges(textModel, false, undefined));251252assertRanges(foldingModel, [r(1, 3, true), r(5, 8, false), r(6, 7, true)]);253} finally {254textModel.dispose();255}256});257258test('delete', () => {259const lines = [260/* 1*/ 'function foo() {',261/* 2*/ ' switch (x) {',262/* 3*/ ' case 1:',263/* 4*/ ' //hello1',264/* 5*/ ' break;',265/* 6*/ ' case 2:',266/* 7*/ ' //hello2',267/* 8*/ ' break;',268/* 9*/ ' case 3:',269/* 10*/ ' //hello3',270/* 11*/ ' break;',271/* 12*/ ' }',272/* 13*/ '}'];273274const textModel = createTextModel(lines.join('\n'));275try {276const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));277278const ranges = computeRanges(textModel, false, undefined);279foldingModel.update(ranges);280281const r1 = r(1, 12, false);282const r2 = r(2, 11, false);283const r3 = r(3, 5, false);284const r4 = r(6, 8, false);285const r5 = r(9, 11, false);286287assertRanges(foldingModel, [r1, r2, r3, r4, r5]);288foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(6)!]);289290textModel.applyEdits([EditOperation.delete(new Range(6, 11, 9, 0))]);291292foldingModel.update(computeRanges(textModel, true, undefined), toSelectedLines([new Selection(7, 1, 7, 1)]));293294assertRanges(foldingModel, [r(1, 9, false), r(2, 8, false), r(3, 5, false), r(6, 8, false)]);295} finally {296textModel.dispose();297}298});299300test('getRegionsInside', () => {301const lines = [302/* 1*/ '/**',303/* 2*/ ' * Comment',304/* 3*/ ' */',305/* 4*/ 'class A {',306/* 5*/ ' void foo() {',307/* 6*/ ' // comment {',308/* 7*/ ' }',309/* 8*/ '}'];310311const textModel = createTextModel(lines.join('\n'));312try {313const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));314315const ranges = computeRanges(textModel, false, undefined);316foldingModel.update(ranges);317318const r1 = r(1, 3, false);319const r2 = r(4, 7, false);320const r3 = r(5, 6, false);321322assertRanges(foldingModel, [r1, r2, r3]);323const region1 = foldingModel.getRegionAtLine(r1.startLineNumber);324const region2 = foldingModel.getRegionAtLine(r2.startLineNumber);325const region3 = foldingModel.getRegionAtLine(r3.startLineNumber);326327assertRegions(foldingModel.getRegionsInside(null), [r1, r2, r3], '1');328assertRegions(foldingModel.getRegionsInside(region1), [], '2');329assertRegions(foldingModel.getRegionsInside(region2), [r3], '3');330assertRegions(foldingModel.getRegionsInside(region3), [], '4');331} finally {332textModel.dispose();333}334335});336337test('getRegionsInsideWithLevel', () => {338const lines = [339/* 1*/ '//#region',340/* 2*/ '//#endregion',341/* 3*/ 'class A {',342/* 4*/ ' void foo() {',343/* 5*/ ' if (true) {',344/* 6*/ ' return;',345/* 7*/ ' }',346/* 8*/ ' if (true) {',347/* 9*/ ' return;',348/* 10*/ ' }',349/* 11*/ ' }',350/* 12*/ '}'];351352const textModel = createTextModel(lines.join('\n'));353try {354355const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));356357const ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });358foldingModel.update(ranges);359360const r1 = r(1, 2, false);361const r2 = r(3, 11, false);362const r3 = r(4, 10, false);363const r4 = r(5, 6, false);364const r5 = r(8, 9, false);365366const region1 = foldingModel.getRegionAtLine(r1.startLineNumber);367const region2 = foldingModel.getRegionAtLine(r2.startLineNumber);368const region3 = foldingModel.getRegionAtLine(r3.startLineNumber);369370assertRanges(foldingModel, [r1, r2, r3, r4, r5]);371372assertRegions(foldingModel.getRegionsInside(null, (r, level) => level === 1), [r1, r2], '1');373assertRegions(foldingModel.getRegionsInside(null, (r, level) => level === 2), [r3], '2');374assertRegions(foldingModel.getRegionsInside(null, (r, level) => level === 3), [r4, r5], '3');375376assertRegions(foldingModel.getRegionsInside(region2, (r, level) => level === 1), [r3], '4');377assertRegions(foldingModel.getRegionsInside(region2, (r, level) => level === 2), [r4, r5], '5');378assertRegions(foldingModel.getRegionsInside(region3, (r, level) => level === 1), [r4, r5], '6');379380assertRegions(foldingModel.getRegionsInside(region2, (r, level) => r.hidesLine(9)), [r3, r5], '7');381382assertRegions(foldingModel.getRegionsInside(region1, (r, level) => level === 1), [], '8');383} finally {384textModel.dispose();385}386387});388389test('getRegionAtLine2', () => {390const lines = [391/* 1*/ '//#region',392/* 2*/ 'class A {',393/* 3*/ ' void foo() {',394/* 4*/ ' if (true) {',395/* 5*/ ' //hello',396/* 6*/ ' }',397/* 7*/ '',398/* 8*/ ' }',399/* 9*/ '}',400/* 10*/ '//#endregion',401/* 11*/ ''];402403const textModel = createTextModel(lines.join('\n'));404try {405const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));406407const ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });408foldingModel.update(ranges);409410const r1 = r(1, 10, false);411const r2 = r(2, 8, false);412const r3 = r(3, 7, false);413const r4 = r(4, 5, false);414415assertRanges(foldingModel, [r1, r2, r3, r4]);416417assertRegions(foldingModel.getAllRegionsAtLine(1), [r1], '1');418assertRegions(foldingModel.getAllRegionsAtLine(2), [r1, r2].reverse(), '2');419assertRegions(foldingModel.getAllRegionsAtLine(3), [r1, r2, r3].reverse(), '3');420assertRegions(foldingModel.getAllRegionsAtLine(4), [r1, r2, r3, r4].reverse(), '4');421assertRegions(foldingModel.getAllRegionsAtLine(5), [r1, r2, r3, r4].reverse(), '5');422assertRegions(foldingModel.getAllRegionsAtLine(6), [r1, r2, r3].reverse(), '6');423assertRegions(foldingModel.getAllRegionsAtLine(7), [r1, r2, r3].reverse(), '7');424assertRegions(foldingModel.getAllRegionsAtLine(8), [r1, r2].reverse(), '8');425assertRegions(foldingModel.getAllRegionsAtLine(9), [r1], '9');426assertRegions(foldingModel.getAllRegionsAtLine(10), [r1], '10');427assertRegions(foldingModel.getAllRegionsAtLine(11), [], '10');428} finally {429textModel.dispose();430}431});432433test('setCollapseStateRecursivly', () => {434const lines = [435/* 1*/ '//#region',436/* 2*/ '//#endregion',437/* 3*/ 'class A {',438/* 4*/ ' void foo() {',439/* 5*/ ' if (true) {',440/* 6*/ ' return;',441/* 7*/ ' }',442/* 8*/ '',443/* 9*/ ' if (true) {',444/* 10*/ ' return;',445/* 11*/ ' }',446/* 12*/ ' }',447/* 13*/ '}'];448449const textModel = createTextModel(lines.join('\n'));450try {451const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));452453const ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });454foldingModel.update(ranges);455456const r1 = r(1, 2, false);457const r2 = r(3, 12, false);458const r3 = r(4, 11, false);459const r4 = r(5, 6, false);460const r5 = r(9, 10, false);461assertRanges(foldingModel, [r1, r2, r3, r4, r5]);462463setCollapseStateLevelsDown(foldingModel, true, Number.MAX_VALUE, [4]);464assertFoldedRanges(foldingModel, [r3, r4, r5], '1');465466setCollapseStateLevelsDown(foldingModel, false, Number.MAX_VALUE, [8]);467assertFoldedRanges(foldingModel, [], '2');468469setCollapseStateLevelsDown(foldingModel, true, Number.MAX_VALUE, [12]);470assertFoldedRanges(foldingModel, [r2, r3, r4, r5], '1');471472setCollapseStateLevelsDown(foldingModel, false, Number.MAX_VALUE, [7]);473assertFoldedRanges(foldingModel, [r2], '1');474475setCollapseStateLevelsDown(foldingModel, false);476assertFoldedRanges(foldingModel, [], '1');477478setCollapseStateLevelsDown(foldingModel, true);479assertFoldedRanges(foldingModel, [r1, r2, r3, r4, r5], '1');480} finally {481textModel.dispose();482}483484});485486test('setCollapseStateAtLevel', () => {487const lines = [488/* 1*/ '//#region',489/* 2*/ '//#endregion',490/* 3*/ 'class A {',491/* 4*/ ' void foo() {',492/* 5*/ ' if (true) {',493/* 6*/ ' return;',494/* 7*/ ' }',495/* 8*/ '',496/* 9*/ ' if (true) {',497/* 10*/ ' return;',498/* 11*/ ' }',499/* 12*/ ' }',500/* 13*/ ' //#region',501/* 14*/ ' const bar = 9;',502/* 15*/ ' //#endregion',503/* 16*/ '}'];504505const textModel = createTextModel(lines.join('\n'));506try {507const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));508509const ranges = computeRanges(textModel, false, { start: /^\s*\/\/#region$/, end: /^\s*\/\/#endregion$/ });510foldingModel.update(ranges);511512const r1 = r(1, 2, false);513const r2 = r(3, 15, false);514const r3 = r(4, 11, false);515const r4 = r(5, 6, false);516const r5 = r(9, 10, false);517const r6 = r(13, 15, false);518assertRanges(foldingModel, [r1, r2, r3, r4, r5, r6]);519520setCollapseStateAtLevel(foldingModel, 1, true, []);521assertFoldedRanges(foldingModel, [r1, r2], '1');522523setCollapseStateAtLevel(foldingModel, 1, false, [5]);524assertFoldedRanges(foldingModel, [r2], '2');525526setCollapseStateAtLevel(foldingModel, 1, false, [1]);527assertFoldedRanges(foldingModel, [], '3');528529setCollapseStateAtLevel(foldingModel, 2, true, []);530assertFoldedRanges(foldingModel, [r3, r6], '4');531532setCollapseStateAtLevel(foldingModel, 2, false, [5, 6]);533assertFoldedRanges(foldingModel, [r3], '5');534535setCollapseStateAtLevel(foldingModel, 3, true, [4, 9]);536assertFoldedRanges(foldingModel, [r3, r4], '6');537538setCollapseStateAtLevel(foldingModel, 3, false, [4, 9]);539assertFoldedRanges(foldingModel, [r3], '7');540} finally {541textModel.dispose();542}543});544545test('setCollapseStateLevelsDown', () => {546const lines = [547/* 1*/ '//#region',548/* 2*/ '//#endregion',549/* 3*/ 'class A {',550/* 4*/ ' void foo() {',551/* 5*/ ' if (true) {',552/* 6*/ ' return;',553/* 7*/ ' }',554/* 8*/ '',555/* 9*/ ' if (true) {',556/* 10*/ ' return;',557/* 11*/ ' }',558/* 12*/ ' }',559/* 13*/ '}'];560561const textModel = createTextModel(lines.join('\n'));562try {563const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));564565const ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });566foldingModel.update(ranges);567568const r1 = r(1, 2, false);569const r2 = r(3, 12, false);570const r3 = r(4, 11, false);571const r4 = r(5, 6, false);572const r5 = r(9, 10, false);573assertRanges(foldingModel, [r1, r2, r3, r4, r5]);574575setCollapseStateLevelsDown(foldingModel, true, 1, [4]);576assertFoldedRanges(foldingModel, [r3], '1');577578setCollapseStateLevelsDown(foldingModel, true, 2, [4]);579assertFoldedRanges(foldingModel, [r3, r4, r5], '2');580581setCollapseStateLevelsDown(foldingModel, false, 2, [3]);582assertFoldedRanges(foldingModel, [r4, r5], '3');583584setCollapseStateLevelsDown(foldingModel, false, 2, [2]);585assertFoldedRanges(foldingModel, [r4, r5], '4');586587setCollapseStateLevelsDown(foldingModel, true, 4, [2]);588assertFoldedRanges(foldingModel, [r1, r4, r5], '5');589590setCollapseStateLevelsDown(foldingModel, false, 4, [2, 3]);591assertFoldedRanges(foldingModel, [], '6');592} finally {593textModel.dispose();594}595});596597test('setCollapseStateLevelsUp', () => {598const lines = [599/* 1*/ '//#region',600/* 2*/ '//#endregion',601/* 3*/ 'class A {',602/* 4*/ ' void foo() {',603/* 5*/ ' if (true) {',604/* 6*/ ' return;',605/* 7*/ ' }',606/* 8*/ '',607/* 9*/ ' if (true) {',608/* 10*/ ' return;',609/* 11*/ ' }',610/* 12*/ ' }',611/* 13*/ '}'];612613const textModel = createTextModel(lines.join('\n'));614try {615const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));616617const ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });618foldingModel.update(ranges);619620const r1 = r(1, 2, false);621const r2 = r(3, 12, false);622const r3 = r(4, 11, false);623const r4 = r(5, 6, false);624const r5 = r(9, 10, false);625assertRanges(foldingModel, [r1, r2, r3, r4, r5]);626627setCollapseStateLevelsUp(foldingModel, true, 1, [4]);628assertFoldedRanges(foldingModel, [r3], '1');629630setCollapseStateLevelsUp(foldingModel, true, 2, [4]);631assertFoldedRanges(foldingModel, [r2, r3], '2');632633setCollapseStateLevelsUp(foldingModel, false, 4, [1, 3, 4]);634assertFoldedRanges(foldingModel, [], '3');635636setCollapseStateLevelsUp(foldingModel, true, 2, [10]);637assertFoldedRanges(foldingModel, [r3, r5], '4');638} finally {639textModel.dispose();640}641642});643644test('setCollapseStateUp', () => {645const lines = [646/* 1*/ '//#region',647/* 2*/ '//#endregion',648/* 3*/ 'class A {',649/* 4*/ ' void foo() {',650/* 5*/ ' if (true) {',651/* 6*/ ' return;',652/* 7*/ ' }',653/* 8*/ '',654/* 9*/ ' if (true) {',655/* 10*/ ' return;',656/* 11*/ ' }',657/* 12*/ ' }',658/* 13*/ '}'];659660const textModel = createTextModel(lines.join('\n'));661try {662const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));663664const ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });665foldingModel.update(ranges);666667const r1 = r(1, 2, false);668const r2 = r(3, 12, false);669const r3 = r(4, 11, false);670const r4 = r(5, 6, false);671const r5 = r(9, 10, false);672assertRanges(foldingModel, [r1, r2, r3, r4, r5]);673674setCollapseStateUp(foldingModel, true, [5]);675assertFoldedRanges(foldingModel, [r4], '1');676677setCollapseStateUp(foldingModel, true, [5]);678assertFoldedRanges(foldingModel, [r3, r4], '2');679680setCollapseStateUp(foldingModel, true, [4]);681assertFoldedRanges(foldingModel, [r2, r3, r4], '2');682} finally {683textModel.dispose();684}685686});687688689test('setCollapseStateForMatchingLines', () => {690const lines = [691/* 1*/ '/**',692/* 2*/ ' * the class',693/* 3*/ ' */',694/* 4*/ 'class A {',695/* 5*/ ' /**',696/* 6*/ ' * the foo',697/* 7*/ ' */',698/* 8*/ ' void foo() {',699/* 9*/ ' /*',700/* 10*/ ' * the comment',701/* 11*/ ' */',702/* 12*/ ' }',703/* 13*/ '}'];704705const textModel = createTextModel(lines.join('\n'));706try {707const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));708709const ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });710foldingModel.update(ranges);711712const r1 = r(1, 3, false);713const r2 = r(4, 12, false);714const r3 = r(5, 7, false);715const r4 = r(8, 11, false);716const r5 = r(9, 11, false);717assertRanges(foldingModel, [r1, r2, r3, r4, r5]);718719const regExp = new RegExp('^\\s*' + escapeRegExpCharacters('/*'));720setCollapseStateForMatchingLines(foldingModel, regExp, true);721assertFoldedRanges(foldingModel, [r1, r3, r5], '1');722} finally {723textModel.dispose();724}725726});727728729test('setCollapseStateForRest', () => {730const lines = [731/* 1*/ '//#region',732/* 2*/ '//#endregion',733/* 3*/ 'class A {',734/* 4*/ ' void foo() {',735/* 5*/ ' if (true) {',736/* 6*/ ' return;',737/* 7*/ ' }',738/* 8*/ '',739/* 9*/ ' if (true) {',740/* 10*/ ' return;',741/* 11*/ ' }',742/* 12*/ ' }',743/* 13*/ '}'];744745const textModel = createTextModel(lines.join('\n'));746try {747const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));748749const ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });750foldingModel.update(ranges);751752const r1 = r(1, 2, false);753const r2 = r(3, 12, false);754const r3 = r(4, 11, false);755const r4 = r(5, 6, false);756const r5 = r(9, 10, false);757assertRanges(foldingModel, [r1, r2, r3, r4, r5]);758759setCollapseStateForRest(foldingModel, true, [5]);760assertFoldedRanges(foldingModel, [r1, r5], '1');761762setCollapseStateForRest(foldingModel, false, [5]);763assertFoldedRanges(foldingModel, [], '2');764765setCollapseStateForRest(foldingModel, true, [1]);766assertFoldedRanges(foldingModel, [r2, r3, r4, r5], '3');767768setCollapseStateForRest(foldingModel, true, [3]);769assertFoldedRanges(foldingModel, [r1, r2, r3, r4, r5], '3');770771} finally {772textModel.dispose();773}774775});776777778test('folding decoration', () => {779const lines = [780/* 1*/ 'class A {',781/* 2*/ ' void foo() {',782/* 3*/ ' if (true) {',783/* 4*/ ' hoo();',784/* 5*/ ' }',785/* 6*/ ' }',786/* 7*/ '}'];787788const textModel = createTextModel(lines.join('\n'));789try {790const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));791792const ranges = computeRanges(textModel, false, undefined);793foldingModel.update(ranges);794795const r1 = r(1, 6, false);796const r2 = r(2, 5, false);797const r3 = r(3, 4, false);798799assertRanges(foldingModel, [r1, r2, r3]);800assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'expanded'), d(3, 'expanded')]);801802foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(2)!]);803804assertRanges(foldingModel, [r1, r(2, 5, true), r3]);805assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'collapsed'), d(3, 'hidden')]);806807foldingModel.update(ranges);808809assertRanges(foldingModel, [r1, r(2, 5, true), r3]);810assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'collapsed'), d(3, 'hidden')]);811812foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)!]);813814assertRanges(foldingModel, [r(1, 6, true), r(2, 5, true), r3]);815assertDecorations(foldingModel, [d(1, 'collapsed'), d(2, 'hidden'), d(3, 'hidden')]);816817foldingModel.update(ranges);818819assertRanges(foldingModel, [r(1, 6, true), r(2, 5, true), r3]);820assertDecorations(foldingModel, [d(1, 'collapsed'), d(2, 'hidden'), d(3, 'hidden')]);821822foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)!, foldingModel.getRegionAtLine(3)!]);823824assertRanges(foldingModel, [r1, r(2, 5, true), r(3, 4, true)]);825assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'collapsed'), d(3, 'hidden')]);826827foldingModel.update(ranges);828829assertRanges(foldingModel, [r1, r(2, 5, true), r(3, 4, true)]);830assertDecorations(foldingModel, [d(1, 'expanded'), d(2, 'collapsed'), d(3, 'hidden')]);831832textModel.dispose();833} finally {834textModel.dispose();835}836837});838839test('fold jumping', () => {840const lines = [841/* 1*/ 'class A {',842/* 2*/ ' void foo() {',843/* 3*/ ' if (1) {',844/* 4*/ ' a();',845/* 5*/ ' } else if (2) {',846/* 6*/ ' if (true) {',847/* 7*/ ' b();',848/* 8*/ ' }',849/* 9*/ ' } else {',850/* 10*/ ' c();',851/* 11*/ ' }',852/* 12*/ ' }',853/* 13*/ '}'854];855856const textModel = createTextModel(lines.join('\n'));857try {858const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));859860const ranges = computeRanges(textModel, false, undefined);861foldingModel.update(ranges);862863const r1 = r(1, 12, false);864const r2 = r(2, 11, false);865const r3 = r(3, 4, false);866const r4 = r(5, 8, false);867const r5 = r(6, 7, false);868const r6 = r(9, 10, false);869assertRanges(foldingModel, [r1, r2, r3, r4, r5, r6]);870871// Test jump to parent.872assert.strictEqual(getParentFoldLine(7, foldingModel), 6);873assert.strictEqual(getParentFoldLine(6, foldingModel), 5);874assert.strictEqual(getParentFoldLine(5, foldingModel), 2);875assert.strictEqual(getParentFoldLine(2, foldingModel), 1);876assert.strictEqual(getParentFoldLine(1, foldingModel), null);877878// Test jump to previous.879assert.strictEqual(getPreviousFoldLine(10, foldingModel), 9);880assert.strictEqual(getPreviousFoldLine(9, foldingModel), 5);881assert.strictEqual(getPreviousFoldLine(5, foldingModel), 3);882assert.strictEqual(getPreviousFoldLine(3, foldingModel), null);883// Test when not on a folding region start line.884assert.strictEqual(getPreviousFoldLine(4, foldingModel), 3);885assert.strictEqual(getPreviousFoldLine(7, foldingModel), 6);886assert.strictEqual(getPreviousFoldLine(8, foldingModel), 6);887888// Test jump to next.889assert.strictEqual(getNextFoldLine(3, foldingModel), 5);890assert.strictEqual(getNextFoldLine(5, foldingModel), 9);891assert.strictEqual(getNextFoldLine(9, foldingModel), null);892// Test when not on a folding region start line.893assert.strictEqual(getNextFoldLine(4, foldingModel), 5);894assert.strictEqual(getNextFoldLine(7, foldingModel), 9);895assert.strictEqual(getNextFoldLine(8, foldingModel), 9);896897} finally {898textModel.dispose();899}900901});902903test('fold jumping issue #129503', () => {904const lines = [905/* 1*/ '',906/* 2*/ 'if True:',907/* 3*/ ' print(1)',908/* 4*/ 'if True:',909/* 5*/ ' print(1)',910/* 6*/ ''911];912913const textModel = createTextModel(lines.join('\n'));914try {915const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));916917const ranges = computeRanges(textModel, false, undefined);918foldingModel.update(ranges);919920const r1 = r(2, 3, false);921const r2 = r(4, 6, false);922assertRanges(foldingModel, [r1, r2]);923924// Test jump to next.925assert.strictEqual(getNextFoldLine(1, foldingModel), 2);926assert.strictEqual(getNextFoldLine(2, foldingModel), 4);927assert.strictEqual(getNextFoldLine(3, foldingModel), 4);928assert.strictEqual(getNextFoldLine(4, foldingModel), null);929assert.strictEqual(getNextFoldLine(5, foldingModel), null);930assert.strictEqual(getNextFoldLine(6, foldingModel), null);931932// Test jump to previous.933assert.strictEqual(getPreviousFoldLine(1, foldingModel), null);934assert.strictEqual(getPreviousFoldLine(2, foldingModel), null);935assert.strictEqual(getPreviousFoldLine(3, foldingModel), 2);936assert.strictEqual(getPreviousFoldLine(4, foldingModel), 2);937assert.strictEqual(getPreviousFoldLine(5, foldingModel), 4);938assert.strictEqual(getPreviousFoldLine(6, foldingModel), 4);939} finally {940textModel.dispose();941}942});943});944945946