From 40bd353361206f45f176c1f8a27943a763111690 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 25 Oct 2017 20:58:11 +0200 Subject: [PATCH] [folding] fix for max folding regions --- src/vs/editor/common/model/indentRanges.ts | 74 ++++++++++--------- .../contrib/folding/common/foldingModel.ts | 20 +++-- .../contrib/folding/test/indentFold.test.ts | 73 ++++++++++++------ .../test/common/model/indentRanges.test.ts | 2 +- 4 files changed, 103 insertions(+), 66 deletions(-) diff --git a/src/vs/editor/common/model/indentRanges.ts b/src/vs/editor/common/model/indentRanges.ts index e913dfd6d52..8217f69b354 100644 --- a/src/vs/editor/common/model/indentRanges.ts +++ b/src/vs/editor/common/model/indentRanges.ts @@ -19,16 +19,14 @@ export class IndentRanges { private _startIndexes: Uint32Array; private _endIndexes: Uint32Array; private _model: ITextModel; - private _indentLimit: number; - constructor(startIndexes: Uint32Array, endIndexes: Uint32Array, model: ITextModel, indentLimit: number) { + constructor(startIndexes: Uint32Array, endIndexes: Uint32Array, model: ITextModel) { if (startIndexes.length !== endIndexes.length || startIndexes.length > MAX_FOLDING_REGIONS) { throw new Error('invalid startIndexes or endIndexes size'); } this._startIndexes = startIndexes; this._endIndexes = endIndexes; this._model = model; - this._indentLimit = indentLimit; this._computeParentIndices(); } @@ -74,10 +72,6 @@ export class IndentRanges { return parent; } - public get indentLimit() { - return this._indentLimit; - } - public getIndent(index: number) { const lineNumber = this.getStartLineNumber(index); const tabSize = this._model.getOptions().tabSize; @@ -132,7 +126,7 @@ export class RangesCollector { private _length: number; private _foldingRegionsLimit: number; - constructor(foldingRegionsLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT) { + constructor(foldingRegionsLimit: number) { this._startIndexes = []; this._endIndexes = []; this._indentOccurrences = []; @@ -153,41 +147,55 @@ export class RangesCollector { } } - private _computeMaxIndent() { - let maxEntries = this._foldingRegionsLimit; - let maxIndent = this._indentOccurrences.length; - for (let i = 0; i < this._indentOccurrences.length; i++) { - if (this._indentOccurrences[i]) { - maxEntries -= this._indentOccurrences[i]; - if (maxEntries < 0) { - maxIndent = i; - break; + public toIndentRanges(model: ITextModel) { + if (this._length <= this._foldingRegionsLimit) { + // reverse and create arrays of the exact length + let startIndexes = new Uint32Array(this._length); + let endIndexes = new Uint32Array(this._length); + for (let i = this._length - 1, k = 0; i >= 0; i-- , k++) { + startIndexes[k] = this._startIndexes[i]; + endIndexes[k] = this._endIndexes[i]; + } + return new IndentRanges(startIndexes, endIndexes, model); + } else { + let entries = 0; + let maxIndent = this._indentOccurrences.length; + for (let i = 0; i < this._indentOccurrences.length; i++) { + let n = this._indentOccurrences[i]; + if (n) { + if (n + entries > this._foldingRegionsLimit) { + maxIndent = i; + break; + } + entries += n; + } + } + const tabSize = model.getOptions().tabSize; + // reverse and create arrays of the exact length + let startIndexes = new Uint32Array(entries); + let endIndexes = new Uint32Array(entries); + for (let i = this._length - 1, k = 0; i >= 0; i--) { + let startIndex = this._startIndexes[i]; + let lineContent = model.getLineContent(startIndex); + let indent = computeIndentLevel(lineContent, tabSize); + if (indent < maxIndent) { + startIndexes[k] = startIndex; + endIndexes[k] = this._endIndexes[i]; + k++; } } + return new IndentRanges(startIndexes, endIndexes, model); } - return maxIndent; - } - public toIndentRanges(model: ITextModel) { - // reverse and create arrays of the exact length - let startIndexes = new Uint32Array(this._length); - let endIndexes = new Uint32Array(this._length); - for (let i = this._length - 1, k = 0; i >= 0; i-- , k++) { - startIndexes[k] = this._startIndexes[i]; - endIndexes[k] = this._endIndexes[i]; - } - return new IndentRanges(startIndexes, endIndexes, model, this._computeMaxIndent()); } } interface PreviousRegion { indent: number; line: number; marker: boolean; }; -export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, minimumRangeSize: number = 1): IndentRanges { - +export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, foldingRegionsLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT): IndentRanges { const tabSize = model.getOptions().tabSize; - - let result = new RangesCollector(); + let result = new RangesCollector(foldingRegionsLimit); let pattern = void 0; if (markers) { @@ -244,7 +252,7 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol // new folding range let endLineNumber = previous.line - 1; - if (endLineNumber - line >= minimumRangeSize) { + if (endLineNumber - line >= 1) { // needs at east size 1 result.insertFirst(line, endLineNumber, indent); } } diff --git a/src/vs/editor/contrib/folding/common/foldingModel.ts b/src/vs/editor/contrib/folding/common/foldingModel.ts index fb1c2b7698b..f0eb43ed984 100644 --- a/src/vs/editor/contrib/folding/common/foldingModel.ts +++ b/src/vs/editor/contrib/folding/common/foldingModel.ts @@ -84,17 +84,15 @@ export class FoldingModel { region.init(ranges, index, isCollapsed); newRegions.push(region); - if (ranges.getIndent(index) < ranges.indentLimit) { - let startLineNumber = region.startLineNumber; - let maxColumn = this._textModel.getLineMaxColumn(startLineNumber); - let decorationRange = { - startLineNumber: startLineNumber, - startColumn: maxColumn, - endLineNumber: startLineNumber, - endColumn: maxColumn - }; - newEditorDecorations.push({ range: decorationRange, options: this._decorationProvider.getDecorationOption(region) }); - } + let startLineNumber = region.startLineNumber; + let maxColumn = this._textModel.getLineMaxColumn(startLineNumber); + let decorationRange = { + startLineNumber: startLineNumber, + startColumn: maxColumn, + endLineNumber: startLineNumber, + endColumn: maxColumn + }; + newEditorDecorations.push({ range: decorationRange, options: this._decorationProvider.getDecorationOption(region) }); }; let k = 0, i = 0; diff --git a/src/vs/editor/contrib/folding/test/indentFold.test.ts b/src/vs/editor/contrib/folding/test/indentFold.test.ts index afefc47ab25..7109691b177 100644 --- a/src/vs/editor/contrib/folding/test/indentFold.test.ts +++ b/src/vs/editor/contrib/folding/test/indentFold.test.ts @@ -5,42 +5,73 @@ 'use strict'; import * as assert from 'assert'; -import { RangesCollector } from 'vs/editor/common/model/indentRanges'; +import { computeRanges } from 'vs/editor/common/model/indentRanges'; +import { Model } from 'vs/editor/common/model/model'; interface IndentRange { startLineNumber: number; endLineNumber: number; - indent: number; } suite('Indentation Folding', () => { - function r(startLineNumber: number, endLineNumber: number, indent: number): IndentRange { - return { startLineNumber, endLineNumber, indent }; + function r(startLineNumber: number, endLineNumber: number): IndentRange { + return { startLineNumber, endLineNumber }; } test('Limit By indent', () => { - let input = [r(1, 4, 0), r(3, 4, 2), r(5, 8, 0), r(6, 7, 1), r(9, 15, 0), r(10, 15, 10), r(11, 12, 2000), r(14, 15, 2000)]; - function assertLimit(maxEntries: number, expectedIndentLimit: number) { - let collector = new RangesCollector(maxEntries); - for (let i of input) { - collector.insertFirst(i.startLineNumber, i.endLineNumber, i.indent); + let lines = [ + /* 1*/ 'A', + /* 2*/ ' A', + /* 3*/ ' A', + /* 4*/ ' A', + /* 5*/ ' A', + /* 6*/ ' A', + /* 7*/ ' A', + /* 8*/ ' A', + /* 9*/ ' A', + /* 10*/ ' A', + /* 11*/ ' A', + /* 12*/ ' A', + /* 13*/ ' A', + /* 14*/ ' A', + /* 15*/ 'A', + /* 16*/ ' A' + ]; + let r1 = r(1, 14); + let r2 = r(3, 11); + let r3 = r(4, 5); + let r4 = r(6, 11); + let r5 = r(8, 9); + let r6 = r(10, 11); + let r7 = r(12, 14); + let r8 = r(13, 14); + let r9 = r(15, 16); + + let model = Model.createFromString(lines.join('\n')); + + function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) { + let indentRanges = computeRanges(model, true, null, maxEntries); + assert.ok(indentRanges.length <= maxEntries, 'max ' + message); + assert.equal(indentRanges.length, expectedRanges.length, 'len ' + message); + for (let i = 0; i < expectedRanges.length; i++) { + assert.equal(indentRanges.getStartLineNumber(i), expectedRanges[i].startLineNumber, 'start ' + message); + assert.equal(indentRanges.getEndLineNumber(i), expectedRanges[i].endLineNumber, 'end ' + message); } - let indentRanges = collector.toIndentRanges(null); - assert.equal(indentRanges.indentLimit, expectedIndentLimit); - assert.ok(input.filter(r => r.indent < expectedIndentLimit).length <= maxEntries); } - assertLimit(8, 11); - assertLimit(7, 11); - assertLimit(6, 11); - assertLimit(5, 10); - assertLimit(4, 2); - assertLimit(3, 1); - assertLimit(2, 0); - assertLimit(1, 0); - assertLimit(0, 0); + assertLimit(1000, [r1, r2, r3, r4, r5, r6, r7, r8, r9], '1'); + assertLimit(9, [r1, r2, r3, r4, r5, r6, r7, r8, r9], '2'); + assertLimit(8, [r1, r2, r3, r4, r5, r6, r7, r9], '3'); + assertLimit(7, [r1, r2, r3, r4, r7, r9], '4'); + assertLimit(6, [r1, r2, r3, r4, r7, r9], '5'); + assertLimit(5, [r1, r2, r7, r9], '6'); + assertLimit(4, [r1, r2, r7, r9], '7'); + assertLimit(3, [r1, r9], '8'); + assertLimit(2, [r1, r9], '9'); + assertLimit(1, [], '10'); + assertLimit(0, [], '11'); }); }); diff --git a/src/vs/editor/test/common/model/indentRanges.test.ts b/src/vs/editor/test/common/model/indentRanges.test.ts index 0e4d401d54d..a703fcc42d5 100644 --- a/src/vs/editor/test/common/model/indentRanges.test.ts +++ b/src/vs/editor/test/common/model/indentRanges.test.ts @@ -330,7 +330,7 @@ suite('Folding with regions', () => { lines.push('#endregion'); } let model = Model.createFromString(lines.join('\n')); - let actual = computeRanges(model, false, markers); + let actual = computeRanges(model, false, markers, MAX_FOLDING_REGIONS); assert.equal(actual.length, nRegions, 'len'); for (let i = 0; i < nRegions; i++) { assert.equal(actual.getStartLineNumber(i), i + 1, 'start' + i); -- GitLab